hermes-json-0.9.0.0: simdjson/singleheader/simdjson.h
/* auto-generated on 2026-10-12 00:12:12 -0400. version 5.0.4 Do not edit! */
/* including simdjson.h: */
/* begin file simdjson.h */
#ifndef SIMDJSON_H
#define SIMDJSON_H
/**
* @mainpage
*
* Check the [README.md](https://github.com/simdjson/simdjson/blob/master/README.md#simdjson--parsing-gigabytes-of-json-per-second).
*
* Sample code. See https://github.com/simdjson/simdjson/blob/master/doc/basics.md for more examples.
#include "simdjson.h"
int main(void) {
// load from `twitter.json` file:
simdjson::dom::parser parser;
simdjson::dom::element tweets = parser.load("twitter.json");
std::cout << tweets["search_metadata"]["count"] << " results." << std::endl;
// Parse and iterate through an array of objects
auto abstract_json = R"( [
{ "12345" : {"a":12.34, "b":56.78, "c": 9998877} },
{ "12545" : {"a":11.44, "b":12.78, "c": 11111111} }
] )"_padded;
for (simdjson::dom::object obj : parser.parse(abstract_json)) {
for(const auto key_value : obj) {
cout << "key: " << key_value.key << " : ";
simdjson::dom::object innerobj = key_value.value;
cout << "a: " << double(innerobj["a"]) << ", ";
cout << "b: " << double(innerobj["b"]) << ", ";
cout << "c: " << int64_t(innerobj["c"]) << endl;
}
}
}
*/
/* including simdjson/common_defs.h: #include "simdjson/common_defs.h" */
/* begin file simdjson/common_defs.h */
#ifndef SIMDJSON_COMMON_DEFS_H
#define SIMDJSON_COMMON_DEFS_H
#include <cassert>
/* including simdjson/compiler_check.h: #include "simdjson/compiler_check.h" */
/* begin file simdjson/compiler_check.h */
#ifndef SIMDJSON_COMPILER_CHECK_H
#define SIMDJSON_COMPILER_CHECK_H
#ifndef __cplusplus
#error simdjson requires a C++ compiler
#endif
#ifndef SIMDJSON_CPLUSPLUS
#if defined(_MSVC_LANG) && !defined(__clang__)
#define SIMDJSON_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
#else
#define SIMDJSON_CPLUSPLUS __cplusplus
#endif
#endif
// C++ 26
// While C++26 is a working draft, compilers report 202400L in C++26 mode
// (both GCC 16 and Clang 21 do). Update when the standard is finalized.
#if !defined(SIMDJSON_CPLUSPLUS26) && (SIMDJSON_CPLUSPLUS >= 202400L)
#define SIMDJSON_CPLUSPLUS26 1
#endif
// C++ 23
#if !defined(SIMDJSON_CPLUSPLUS23) && (SIMDJSON_CPLUSPLUS >= 202302L)
#define SIMDJSON_CPLUSPLUS23 1
#endif
// C++ 20
#if !defined(SIMDJSON_CPLUSPLUS20) && (SIMDJSON_CPLUSPLUS >= 202002L)
#define SIMDJSON_CPLUSPLUS20 1
#endif
// C++ 17
#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L)
#define SIMDJSON_CPLUSPLUS17 1
#endif
// C++ 14
#if !defined(SIMDJSON_CPLUSPLUS14) && (SIMDJSON_CPLUSPLUS >= 201402L)
#define SIMDJSON_CPLUSPLUS14 1
#endif
// C++ 11
#if !defined(SIMDJSON_CPLUSPLUS11) && (SIMDJSON_CPLUSPLUS >= 201103L)
#define SIMDJSON_CPLUSPLUS11 1
#endif
#ifndef SIMDJSON_CPLUSPLUS11
#error simdjson requires a compiler compliant with the C++11 standard
#endif
#ifndef SIMDJSON_IF_CONSTEXPR
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_IF_CONSTEXPR if constexpr
#else
#define SIMDJSON_IF_CONSTEXPR if
#endif
#endif
#ifndef SIMDJSON_CONSTEXPR_LAMBDA
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_CONSTEXPR_LAMBDA constexpr
#else
#define SIMDJSON_CONSTEXPR_LAMBDA
#endif
#endif
#ifdef __has_include
#if __has_include(<version>)
#include <version>
#endif
#endif
// Static reflection.
//
// The reflection-based APIs (simdjson::to, document::get<T>, the builder,
// compile-time JSON, annotations) need considerably more than the reflection
// operator. We turn them on only when the compiler advertises all of:
//
// P2996 reflection (^^, splicers, <meta>) __cpp_impl_reflection,
// __cpp_lib_reflection
// P1306 expansion statements (template for) __cpp_expansion_statements
// P3491 std::define_static_string / _array __cpp_lib_define_static
//
// Two further features we rely on have, as of this writing, no feature-test
// macro of their own, so they cannot be checked directly:
//
// P3394 annotations ([[=x]], std::meta::annotations_of) -- used for
// the annotations of simdjson/annotations.h (rename, skip, ...).
// P3289 consteval blocks (consteval { ... }) -- used by compile_time_json.
//
// Every implementation that defines the four macros above also implements
// those two, so requiring the four is sufficient in practice. If that ever
// stops being true, define SIMDJSON_STATIC_REFLECTION=0 to opt out.
//
// SIMDJSON_STATIC_REFLECTION may always be defined by the user (or by the
// build system) to 0 or 1 to override the detection.
//
// Note that C++26 mode alone is not enough: GCC 16 requires -freflection,
// and only then does it define __cpp_impl_reflection.
#ifndef SIMDJSON_STATIC_REFLECTION
#if defined(SIMDJSON_CPLUSPLUS26) && \
defined(__cpp_impl_reflection) && __cpp_impl_reflection >= 202506L && \
defined(__cpp_lib_reflection) && __cpp_lib_reflection >= 202506L && \
defined(__cpp_expansion_statements) && \
__cpp_expansion_statements >= 202506L && \
defined(__cpp_lib_define_static) && __cpp_lib_define_static >= 202506L
// __cpp_lib_reflection is the feature-test macro for <meta>, so there is no
// need for a separate __has_include check (which would have to be guarded for
// compilers that lack __has_include).
#define SIMDJSON_STATIC_REFLECTION 1
#else
#define SIMDJSON_STATIC_REFLECTION 0
#endif
#endif // SIMDJSON_STATIC_REFLECTION
#if defined(__apple_build_version__)
#if __apple_build_version__ < 14000000
#define SIMDJSON_CONCEPT_DISABLED 1 // apple-clang/13 doesn't support std::convertible_to
#endif
#endif
#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
#include <ranges>
#define SIMDJSON_SUPPORTS_RANGES 1
#else
#define SIMDJSON_SUPPORTS_RANGES 0
#endif
#if defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED)
#if __cpp_concepts >= 201907L
#include <utility>
#define SIMDJSON_SUPPORTS_CONCEPTS 1
#else
#define SIMDJSON_SUPPORTS_CONCEPTS 0
#endif
#else // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED)
#define SIMDJSON_SUPPORTS_CONCEPTS 0
#endif // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED)
// copy SIMDJSON_SUPPORTS_CONCEPTS to SIMDJSON_SUPPORTS_DESERIALIZATION.
#if SIMDJSON_SUPPORTS_CONCEPTS
#define SIMDJSON_SUPPORTS_DESERIALIZATION 1
#else
#define SIMDJSON_SUPPORTS_DESERIALIZATION 0
#endif
// The C++20 char8_t type (and std::u8string/std::u8string_view) is available.
// Because all strings that simdjson produces are valid UTF-8, we can offer
// char8_t variants of our string accessors when this macro is set.
#if !defined(SIMDJSON_SUPPORTS_CHAR8_T)
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
#define SIMDJSON_SUPPORTS_CHAR8_T 1
#else
#define SIMDJSON_SUPPORTS_CHAR8_T 0
#endif
#endif // !defined(SIMDJSON_SUPPORTS_CHAR8_T)
// The C++23 fixed-width floating-point types std::float32_t and std::float64_t
// (<stdfloat>) are available. They are optional even in C++23: a compiler that
// provides them predefines __STDCPP_FLOAT32_T__ and __STDCPP_FLOAT64_T__.
// When these macros are set, we offer get_float32() and get_float64().
#if !defined(SIMDJSON_SUPPORTS_FLOAT32_T)
#if defined(__STDCPP_FLOAT32_T__) && defined(__has_include)
#if __has_include(<stdfloat>)
#define SIMDJSON_SUPPORTS_FLOAT32_T 1
#endif
#endif
#ifndef SIMDJSON_SUPPORTS_FLOAT32_T
#define SIMDJSON_SUPPORTS_FLOAT32_T 0
#endif
#endif // !defined(SIMDJSON_SUPPORTS_FLOAT32_T)
#if !defined(SIMDJSON_SUPPORTS_FLOAT64_T)
#if defined(__STDCPP_FLOAT64_T__) && defined(__has_include)
#if __has_include(<stdfloat>)
#define SIMDJSON_SUPPORTS_FLOAT64_T 1
#endif
#endif
#ifndef SIMDJSON_SUPPORTS_FLOAT64_T
#define SIMDJSON_SUPPORTS_FLOAT64_T 0
#endif
#endif // !defined(SIMDJSON_SUPPORTS_FLOAT64_T)
#if SIMDJSON_SUPPORTS_FLOAT32_T || SIMDJSON_SUPPORTS_FLOAT64_T
#include <stdfloat>
#endif
#if !defined(SIMDJSON_CONSTEVAL)
#if defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
#define SIMDJSON_CONSTEVAL 1
#else
#define SIMDJSON_CONSTEVAL 0
#endif // defined(__cpp_consteval) && __cpp_consteval >= 201811L && defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
#endif // !defined(SIMDJSON_CONSTEVAL)
// SIMDJSON_CONSTEXPR_STRING is 'constexpr' when the standard library supports
// constexpr std::string (e.g., libstdc++ 12 or better), and empty otherwise. It
// lets functions that build a std::string be constant expressions when possible
// while still compiling against older standard libraries.
#if !defined(SIMDJSON_CONSTEXPR_STRING)
#if defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
#define SIMDJSON_CONSTEXPR_STRING constexpr
#else
#define SIMDJSON_CONSTEXPR_STRING
#endif // defined(__cpp_lib_constexpr_string) && __cpp_lib_constexpr_string >= 201907L
#endif // !defined(SIMDJSON_CONSTEXPR_STRING)
#endif // SIMDJSON_COMPILER_CHECK_H
/* end file simdjson/compiler_check.h */
/* including simdjson/portability.h: #include "simdjson/portability.h" */
/* begin file simdjson/portability.h */
#ifndef SIMDJSON_PORTABILITY_H
#define SIMDJSON_PORTABILITY_H
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cfloat>
#include <cassert>
#include <climits>
#ifndef _WIN32
// strcasecmp, strncasecmp
#include <strings.h>
#endif
static_assert(CHAR_BIT == 8, "simdjson requires 8-bit bytes");
// We are using size_t without namespace std:: throughout the project
using std::size_t;
#ifdef _MSC_VER
#define SIMDJSON_VISUAL_STUDIO 1
/**
* We want to differentiate carefully between
* clang under visual studio and regular visual
* studio.
*
* Under clang for Windows, we enable:
* * target pragmas so that part and only part of the
* code gets compiled for advanced instructions.
*
*/
#ifdef __clang__
// clang under visual studio
#define SIMDJSON_CLANG_VISUAL_STUDIO 1
#else
// just regular visual studio (best guess)
#define SIMDJSON_REGULAR_VISUAL_STUDIO 1
#endif // __clang__
#endif // _MSC_VER
#if (defined(__x86_64__) || defined(_M_AMD64)) && !defined(_M_ARM64EC)
#define SIMDJSON_IS_X86_64 1
#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
#define SIMDJSON_IS_ARM64 1
#elif defined(__riscv) && __riscv_xlen == 64
#define SIMDJSON_IS_RISCV64 1
#if __riscv_v_intrinsic >= 11000
#define SIMDJSON_HAS_RVV_INTRINSICS 1
#endif
#if SIMDJSON_HAS_RVV_INTRINSICS && __riscv_vector && __riscv_v_min_vlen >= 128 && __riscv_v_elen >= 64
#define SIMDJSON_IS_RVV 1 // RISC-V V extension
#endif
// current toolchains don't support fixed-size SIMD types that don't match VLEN directly
#if __riscv_v_fixed_vlen >= 128 && __riscv_v_fixed_vlen <= 512
#define SIMDJSON_IS_RVV_VLS 1
#endif
#elif defined(__loongarch_lp64)
#define SIMDJSON_IS_LOONGARCH64 1
#if defined(__loongarch_sx) && defined(__loongarch_asx)
#define SIMDJSON_IS_LSX 1
#define SIMDJSON_IS_LASX 1 // We can always run both
#elif defined(__loongarch_sx)
#define SIMDJSON_IS_LSX 1
// Adjust for runtime dispatching support.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__NVCOMPILER)
#if __GNUC__ > 15 || (__GNUC__ == 15 && __GNUC_MINOR__ >= 0)
// We are ok, we will support runtime dispatch for LASX.
#else
// We disable runtime dispatch for LASX, which means that we will not be able to use LASX
// even if it is supported by the hardware.
// Loongson users should update to GCC 15 or better.
#define SIMDJSON_IMPLEMENTATION_LASX 0
#endif
#else
// We are not using GCC, so we assume that we can support runtime dispatch for LASX.
// https://godbolt.org/z/jcMnrjYhs
#define SIMDJSON_IMPLEMENTATION_LASX 0
#endif
#endif
#elif defined(__PPC64__) || defined(_M_PPC64)
#define SIMDJSON_IS_PPC64 1
#if defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#define SIMDJSON_IS_PPC64_VMX 1
#endif // defined(__ALTIVEC__)
#else
#define SIMDJSON_IS_32BITS 1
#if defined(_M_IX86) || defined(__i386__)
#define SIMDJSON_IS_X86_32BITS 1
#elif defined(__arm__) || defined(_M_ARM)
#define SIMDJSON_IS_ARM_32BITS 1
#elif defined(__PPC__) || defined(_M_PPC)
#define SIMDJSON_IS_PPC_32BITS 1
#endif
#endif // defined(__x86_64__) || defined(_M_AMD64)
#ifndef SIMDJSON_IS_32BITS
#define SIMDJSON_IS_32BITS 0
#endif
#if SIMDJSON_IS_32BITS
#ifndef SIMDJSON_NO_PORTABILITY_WARNING
// In the future, we should allow programmers
// to get warning.
#endif // SIMDJSON_NO_PORTABILITY_WARNING
#endif // SIMDJSON_IS_32BITS
#define SIMDJSON_CAT_IMPLEMENTATION_(a,...) a ## __VA_ARGS__
#define SIMDJSON_CAT(a,...) SIMDJSON_CAT_IMPLEMENTATION_(a, __VA_ARGS__)
#define SIMDJSON_STRINGIFY_IMPLEMENTATION_(a,...) #a SIMDJSON_STRINGIFY(__VA_ARGS__)
#define SIMDJSON_STRINGIFY(a,...) SIMDJSON_CAT_IMPLEMENTATION_(a, __VA_ARGS__)
// this is almost standard?
#undef SIMDJSON_STRINGIFY_IMPLEMENTATION_
#undef SIMDJSON_STRINGIFY
#define SIMDJSON_STRINGIFY_IMPLEMENTATION_(a) #a
#define SIMDJSON_STRINGIFY(a) SIMDJSON_STRINGIFY_IMPLEMENTATION_(a)
// Our fast kernels require 64-bit systems.
//
// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions.
// Furthermore, the number of SIMD registers is reduced.
//
// On 32-bit ARM, we would have smaller registers.
//
// The simdjson users should still have the fallback kernel. It is
// slower, but it should run everywhere.
//
// Enable valid runtime implementations, and select SIMDJSON_BUILTIN_IMPLEMENTATION
//
// We are going to use runtime dispatch.
#if defined(SIMDJSON_IS_X86_64) || defined(SIMDJSON_IS_LSX)
#ifdef __clang__
// clang does not have GCC push pop
// warning: clang attribute push can't be used within a namespace in clang up
// til 8.0 so SIMDJSON_TARGET_REGION and SIMDJSON_UNTARGET_REGION must be *outside* of a
// namespace.
#define SIMDJSON_TARGET_REGION(T) \
_Pragma(SIMDJSON_STRINGIFY( \
clang attribute push(__attribute__((target(T))), apply_to = function)))
#define SIMDJSON_UNTARGET_REGION _Pragma("clang attribute pop")
#elif defined(__GNUC__)
// GCC is easier
#define SIMDJSON_TARGET_REGION(T) \
_Pragma("GCC push_options") _Pragma(SIMDJSON_STRINGIFY(GCC target(T)))
#define SIMDJSON_UNTARGET_REGION _Pragma("GCC pop_options")
#endif // clang then gcc
#endif // defined(SIMDJSON_IS_X86_64) || defined(SIMDJSON_IS_LSX)
// Default target region macros don't do anything.
#ifndef SIMDJSON_TARGET_REGION
#define SIMDJSON_TARGET_REGION(T)
#define SIMDJSON_UNTARGET_REGION
#endif
// Is threading enabled?
#if defined(_REENTRANT) || defined(_MT)
#ifndef SIMDJSON_THREADS_ENABLED
#define SIMDJSON_THREADS_ENABLED
#endif
#endif
// workaround for large stack sizes under -O0.
// https://github.com/simdjson/simdjson/issues/691
#ifdef __APPLE__
#ifndef __OPTIMIZE__
// Apple systems have small stack sizes in secondary threads.
// Lack of compiler optimization may generate high stack usage.
// Users may want to disable threads for safety, but only when
// in debug mode which we detect by the fact that the __OPTIMIZE__
// macro is not defined.
#undef SIMDJSON_THREADS_ENABLED
#endif
#endif
#if defined(__clang__)
#define SIMDJSON_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined")))
#elif defined(__GNUC__)
#define SIMDJSON_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined))
#else
#define SIMDJSON_NO_SANITIZE_UNDEFINED
#endif
#if defined(__clang__) || defined(__GNUC__)
#define simdjson_pure [[gnu::pure]]
#else
#define simdjson_pure
#endif
#if defined(__clang__) || defined(__GNUC__)
#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
#define SIMDJSON_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
# endif // if __has_feature(memory_sanitizer)
#endif // defined(__has_feature)
#endif
// make sure it is defined as 'nothing' if it is unapplicable.
#ifndef SIMDJSON_NO_SANITIZE_MEMORY
#define SIMDJSON_NO_SANITIZE_MEMORY
#endif
#if SIMDJSON_VISUAL_STUDIO
// This is one case where we do not distinguish between
// regular visual studio and clang under visual studio.
// clang under Windows has _stricmp (like visual studio) but not strcasecmp (as clang normally has)
#define simdjson_strcasecmp _stricmp
#define simdjson_strncasecmp _strnicmp
#else
// The strcasecmp, strncasecmp, and strcasestr functions do not work with multibyte strings (e.g. UTF-8).
// So they are only useful for ASCII in our context.
// https://www.gnu.org/software/libunistring/manual/libunistring.html#char-_002a-strings
#define simdjson_strcasecmp strcasecmp
#define simdjson_strncasecmp strncasecmp
#endif
#if (defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG))) && !SIMDJSON_DEVELOPMENT_CHECKS
// If SIMDJSON_DEVELOPMENT_CHECKS is undefined or 0, we consider that we are in release mode.
// If NDEBUG is set, or __OPTIMIZE__ is set, or we are under MSVC in release mode,
// then do away with asserts and use __assume.
// We still recommend that our users set NDEBUG in release mode.
#if SIMDJSON_VISUAL_STUDIO
#define SIMDJSON_UNREACHABLE() __assume(0)
#define SIMDJSON_ASSUME(COND) __assume(COND)
#else
#define SIMDJSON_UNREACHABLE() __builtin_unreachable();
#define SIMDJSON_ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0)
#endif
#else // defined(NDEBUG) || defined(__OPTIMIZE__) || (defined(_MSC_VER) && !defined(_DEBUG)) && !SIMDJSON_DEVELOPMENT_CHECKS
// This should only ever be enabled in debug mode.
#define SIMDJSON_UNREACHABLE() assert(0);
#define SIMDJSON_ASSUME(COND) assert(COND)
#endif
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
#define SIMDJSON_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#elif defined _WIN32
#define SIMDJSON_IS_BIG_ENDIAN 0
#else
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <machine/endian.h>
#elif defined(sun) || defined(__sun)
#include <sys/byteorder.h>
#elif defined(__MVS__)
#include <sys/endian.h>
#else
#ifdef __has_include
#if __has_include(<endian.h>)
#include <endian.h>
#endif //__has_include(<endian.h>)
#endif //__has_include
#endif
#
#ifndef __BYTE_ORDER__
// safe choice
#define SIMDJSON_IS_BIG_ENDIAN 0
#endif
#
#ifndef __ORDER_LITTLE_ENDIAN__
// safe choice
#define SIMDJSON_IS_BIG_ENDIAN 0
#endif
#
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define SIMDJSON_IS_BIG_ENDIAN 0
#else
#define SIMDJSON_IS_BIG_ENDIAN 1
#endif
#endif
#ifndef SIMDJSON_HAS_UNISTD_H
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
#define SIMDJSON_HAS_UNISTD_H 1
#else
#define SIMDJSON_HAS_UNISTD_H 0
#endif
#endif
// padded_memory_map availability.
//
// On POSIX platforms the class is always available: the implementation uses
// `mmap` (and a trailing anonymous page for padding) from <sys/mman.h>.
//
// On Windows the class is disabled by default and must be explicitly
// opted into by defining `SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1`. Enabling
// it requires:
// 1. `<windows.h>` has been included *before* `<simdjson.h>` (so that
// this header can see the Win32 types and the `_WINDOWS_` include
// guard),
// 2. the compilation targets Windows 10, version 1803 or later
// (i.e. `NTDDI_VERSION >= NTDDI_WIN10_RS4`, `0x0A000005`). This is
// required because the implementation relies on the modern memory
// APIs introduced with that version (`CreateFileMapping2` /
// `MapViewOfFile3`),
// 3. the link step pulls in an import library that exports those APIs,
// typically `onecore.lib` (or `mincore.lib`).
//
// The `SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS` CMake option arranges (1)-(3)
// automatically when building simdjson with its own CMake. Consumers using
// simdjson as a pre-built library are responsible for setting the macro,
// the Windows version macros, and the link library themselves.
//
// If the opt-in conditions are not met on Windows, `padded_memory_map`
// simply does not exist -- any attempt to use it fails at compile time
// with an "unknown identifier" diagnostic rather than silently degrading.
//
// The SIMDJSON_HAS_PADDED_MEMORY_MAP macro reflects whether the class is
// available in the current translation unit. Users may test this macro to
// conditionally compile code that depends on padded_memory_map.
#ifndef SIMDJSON_HAS_PADDED_MEMORY_MAP
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
#define SIMDJSON_HAS_PADDED_MEMORY_MAP 1
#elif defined(_WINDOWS_) && defined(SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS) && SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS
#define SIMDJSON_HAS_PADDED_MEMORY_MAP 1
#else
#define SIMDJSON_HAS_PADDED_MEMORY_MAP 0
#endif
#endif
#endif // SIMDJSON_PORTABILITY_H
/* end file simdjson/portability.h */
#include <cstddef>
namespace simdjson {
namespace internal {
/**
* @private
* Scratch capacity that every caller of to_chars must provide.
*
* The emitted decimal is at most ~24 characters, but dragonbox() and
* format_buffer() intentionally write past the logical end with fixed-size
* 16/17-byte memcpy/memset operations so the compiler can inline them (no
* libc mem* dispatch with size-class branches). The extra bytes are required
* for safety of those over-writes; do not shrink this below 40.
* See src/to_chars.cpp and #2805.
*/
// Use an unscoped enum (not static constexpr / inline constexpr):
// - C++11 targets (readme_examples11, quickstart11, ...) still include this header
// - a static constexpr in the amalgamated simdjson.cpp TU is unused there
// (only callers in headers use it) and trips -Wunused-const-variable -Werror
enum : size_t { to_chars_buffer_size = 40 };
/**
* @private
* Our own implementation of the C++17 to_chars function.
* Defined in src/to_chars
*
* @note The buffer starting at first must have at least to_chars_buffer_size
* bytes of writable storage (see to_chars_buffer_size).
* @note The input number must be finite (NaN/Inf are not supported).
* @note The result is NOT null-terminated.
*/
char *to_chars(char *first, const char *last, double value);
/**
* @private
* A number parsing routine.
* Defined in src/from_chars
*/
double from_chars(const char *first) noexcept;
double from_chars(const char *first, const char* end) noexcept;
/**
* @private
* Same as from_chars, but produces a correctly rounded binary32 (float) value.
* Defined in src/from_chars
*/
float from_chars_float(const char *first) noexcept;
}
#ifndef SIMDJSON_EXCEPTIONS
#if defined(__cpp_exceptions) || defined(_CPPUNWIND)
#define SIMDJSON_EXCEPTIONS 1
#else
#define SIMDJSON_EXCEPTIONS 0
#endif
#endif
#ifndef SIMDJSON_ENABLE_NAN_INF
#define SIMDJSON_ENABLE_NAN_INF 0
#endif
} // namespace simdjson
#if defined(__GNUC__)
// Marks a block with a name so that MCA analysis can see it.
#define SIMDJSON_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
#define SIMDJSON_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
#define SIMDJSON_DEBUG_BLOCK(name, block) BEGIN_DEBUG_BLOCK(name); block; END_DEBUG_BLOCK(name);
#else
#define SIMDJSON_BEGIN_DEBUG_BLOCK(name)
#define SIMDJSON_END_DEBUG_BLOCK(name)
#define SIMDJSON_DEBUG_BLOCK(name, block)
#endif
// Align to N-byte boundary
#define SIMDJSON_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1))
#if SIMDJSON_REGULAR_VISUAL_STUDIO
// We could use [[deprecated]] but it requires C++14
#define simdjson_deprecated __declspec(deprecated)
#define simdjson_really_inline __forceinline
#define simdjson_never_inline inline __declspec(noinline)
#define simdjson_really_flatten [[msvc::flatten]]
#define simdjson_unused
#define simdjson_warn_unused
#ifndef simdjson_likely
#define simdjson_likely(x) x
#endif
#ifndef simdjson_unlikely
#define simdjson_unlikely(x) x
#endif
#define SIMDJSON_PUSH_DISABLE_WARNINGS __pragma(warning( push ))
#define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS __pragma(warning( push, 0 ))
#define SIMDJSON_DISABLE_VS_WARNING(WARNING_NUMBER) __pragma(warning( disable : WARNING_NUMBER ))
// Get rid of Intellisense-only warnings (Code Analysis)
// Though __has_include is C++17, it is supported in Visual Studio 2017 or better (_MSC_VER>=1910).
#ifdef __has_include
#if __has_include(<CppCoreCheck\Warnings.h>)
#include <CppCoreCheck\Warnings.h>
#define SIMDJSON_DISABLE_UNDESIRED_WARNINGS SIMDJSON_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
#endif
#endif
#ifndef SIMDJSON_DISABLE_UNDESIRED_WARNINGS
#define SIMDJSON_DISABLE_UNDESIRED_WARNINGS
#endif
#define SIMDJSON_DISABLE_DEPRECATED_WARNING SIMDJSON_DISABLE_VS_WARNING(4996)
#define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
#define SIMDJSON_POP_DISABLE_WARNINGS __pragma(warning( pop ))
#define SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
#define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
// We could use [[deprecated]] but it requires C++14
#define simdjson_deprecated __attribute__((deprecated))
#define simdjson_really_inline inline __attribute__((always_inline))
#define simdjson_never_inline inline __attribute__((noinline))
#define simdjson_really_flatten [[gnu::flatten]]
#define simdjson_unused __attribute__((unused))
#define simdjson_warn_unused __attribute__((warn_unused_result))
#ifndef simdjson_likely
#define simdjson_likely(x) __builtin_expect(!!(x), 1)
#endif
#ifndef simdjson_unlikely
#define simdjson_unlikely(x) __builtin_expect(!!(x), 0)
#endif
#define SIMDJSON_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
// gcc doesn't seem to disable all warnings with all and extra, add warnings here as necessary
// We do it separately for clang since it has different warnings.
#ifdef __clang__
// clang is missing -Wmaybe-uninitialized.
#define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \
SIMDJSON_DISABLE_GCC_WARNING(-Weffc++) \
SIMDJSON_DISABLE_GCC_WARNING(-Wall) \
SIMDJSON_DISABLE_GCC_WARNING(-Wconversion) \
SIMDJSON_DISABLE_GCC_WARNING(-Wextra) \
SIMDJSON_DISABLE_GCC_WARNING(-Wattributes) \
SIMDJSON_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \
SIMDJSON_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \
SIMDJSON_DISABLE_GCC_WARNING(-Wreturn-type) \
SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \
SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \
SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable)
#else // __clang__
#define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \
SIMDJSON_DISABLE_GCC_WARNING(-Weffc++) \
SIMDJSON_DISABLE_GCC_WARNING(-Wall) \
SIMDJSON_DISABLE_GCC_WARNING(-Wconversion) \
SIMDJSON_DISABLE_GCC_WARNING(-Wextra) \
SIMDJSON_DISABLE_GCC_WARNING(-Wattributes) \
SIMDJSON_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \
SIMDJSON_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \
SIMDJSON_DISABLE_GCC_WARNING(-Wreturn-type) \
SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \
SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \
SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) \
SIMDJSON_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) \
SIMDJSON_DISABLE_GCC_WARNING(-Wformat-security)
#endif // __clang__
#define SIMDJSON_PRAGMA(P) _Pragma(#P)
#define SIMDJSON_DISABLE_GCC_WARNING(WARNING) SIMDJSON_PRAGMA(GCC diagnostic ignored #WARNING)
#if SIMDJSON_CLANG_VISUAL_STUDIO
#define SIMDJSON_DISABLE_UNDESIRED_WARNINGS SIMDJSON_DISABLE_GCC_WARNING(-Wmicrosoft-include)
#else
#define SIMDJSON_DISABLE_UNDESIRED_WARNINGS
#endif
#define SIMDJSON_DISABLE_DEPRECATED_WARNING SIMDJSON_DISABLE_GCC_WARNING(-Wdeprecated-declarations)
#define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING SIMDJSON_DISABLE_GCC_WARNING(-Wstrict-overflow)
#define SIMDJSON_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
#define SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \
SIMDJSON_DISABLE_GCC_WARNING(-Wunused)
#define SIMDJSON_POP_DISABLE_UNUSED_WARNINGS SIMDJSON_POP_DISABLE_WARNINGS
#endif // MSC_VER
#if defined(simdjson_inline)
// Prefer the user's definition of simdjson_inline; don't define it ourselves.
#elif defined(__GNUC__) && !defined(__OPTIMIZE__)
// If optimizations are disabled, forcing inlining can lead to significant
// code bloat and high compile times. Don't use simdjson_really_inline for
// unoptimized builds.
#define simdjson_inline inline
#else
// Force inlining for most simdjson functions.
#define simdjson_inline simdjson_really_inline
#endif
#if defined(simdjson_flatten)
// Prefer the user's definition of simdjson_flatten; don't define it ourselves.
#elif (defined(__GNUC__) && !defined(__OPTIMIZE__)) || (defined(_DEBUG) && _MSC_VER )
// Flattening can lead to significant code bloat and high compile times. Don't use it for unoptimized builds.
#define simdjson_flatten
#else
#define simdjson_flatten simdjson_really_flatten
#endif
#if SIMDJSON_VISUAL_STUDIO
/**
* Windows users need to do some extra work when building
* or using a dynamic library (DLL). When building, we need
* to set SIMDJSON_DLLIMPORTEXPORT to __declspec(dllexport).
* When *using* the DLL, the user needs to set
* SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport).
*
* Static libraries not need require such work.
*
* It does not matter here whether you are using
* the regular visual studio or clang under visual
* studio, you still need to handle these issues.
*
* Non-Windows systems do not have this complexity.
*/
#if SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY
// We set SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY when we build a DLL under Windows.
// It should never happen that both SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY and
// SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY are set.
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport)
#elif SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY
// Windows user who call a dynamic library should set SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY to 1.
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
#else
// We assume by default static linkage
#define SIMDJSON_DLLIMPORTEXPORT
#endif
#else
#define SIMDJSON_DLLIMPORTEXPORT
#endif
// C++17 requires string_view.
#if SIMDJSON_CPLUSPLUS17
#define SIMDJSON_HAS_STRING_VIEW
#include <string_view> // by the standard, this has to be safe.
#endif
// This macro (__cpp_lib_string_view) has to be defined
// for C++17 and better, but if it is otherwise defined,
// we are going to assume that string_view is available
// even if we do not have C++17 support.
#ifdef __cpp_lib_string_view
#define SIMDJSON_HAS_STRING_VIEW
#include <string_view>
#endif
// Some systems have string_view even if we do not have C++17 support,
// and even if __cpp_lib_string_view is undefined, it is the case
// with Apple clang version 11.
// We must handle it. *This is important.*
#ifndef _MSC_VER
#ifndef SIMDJSON_HAS_STRING_VIEW
#if defined __has_include
// do not combine the next #if with the previous one (unsafe)
#if __has_include (<string_view>)
// now it is safe to trigger the include
#include <string_view> // though the file is there, it does not follow that we got the implementation
#if defined(_LIBCPP_STRING_VIEW)
// Ah! So we under libc++ which under its Library Fundamentals Technical Specification, which preceded C++17,
// included string_view.
// This means that we have string_view *even though* we may not have C++17.
#define SIMDJSON_HAS_STRING_VIEW
#endif // _LIBCPP_STRING_VIEW
#endif // __has_include (<string_view>)
#endif // defined __has_include
#endif // def SIMDJSON_HAS_STRING_VIEW
#endif // def _MSC_VER
// end of complicated but important routine to try to detect string_view.
//
// Backfill std::string_view using nonstd::string_view on systems where
// we expect that string_view is missing. Important: if we get this wrong,
// we will end up with two string_view definitions and potential trouble.
// That is why we work so hard above to avoid it.
//
#ifndef SIMDJSON_HAS_STRING_VIEW
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
/* including simdjson/nonstd/string_view.hpp: #include "simdjson/nonstd/string_view.hpp" */
/* begin file simdjson/nonstd/string_view.hpp */
// Copyright 2017-2020 by Martin Moene
//
// string-view lite, a C++17-like string_view for C++98 and later.
// For more information see https://github.com/martinmoene/string-view-lite
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef NONSTD_SV_LITE_H_INCLUDED
#define NONSTD_SV_LITE_H_INCLUDED
#define string_view_lite_MAJOR 1
#define string_view_lite_MINOR 8
#define string_view_lite_PATCH 0
#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x )
#define nssv_STRINGIFY_( x ) #x
// string-view lite configuration:
#define nssv_STRING_VIEW_DEFAULT 0
#define nssv_STRING_VIEW_NONSTD 1
#define nssv_STRING_VIEW_STD 2
// tweak header support:
#ifdef __has_include
# if __has_include(<nonstd/string_view.tweak.hpp>)
# include <nonstd/string_view.tweak.hpp>
# endif
#define nssv_HAVE_TWEAK_HEADER 1
#else
#define nssv_HAVE_TWEAK_HEADER 0
//# pragma message("string_view.hpp: Note: Tweak header not supported.")
#endif
// string_view selection and configuration:
#if !defined( nssv_CONFIG_SELECT_STRING_VIEW )
# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD )
#endif
#ifndef nssv_CONFIG_STD_SV_OPERATOR
# define nssv_CONFIG_STD_SV_OPERATOR 0
#endif
#ifndef nssv_CONFIG_USR_SV_OPERATOR
# define nssv_CONFIG_USR_SV_OPERATOR 1
#endif
#ifdef nssv_CONFIG_CONVERSION_STD_STRING
# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING
# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING
#endif
#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1
#endif
#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1
#endif
#ifndef nssv_CONFIG_NO_STREAM_INSERTION
# define nssv_CONFIG_NO_STREAM_INSERTION 0
#endif
#ifndef nssv_CONFIG_CONSTEXPR11_STD_SEARCH
# define nssv_CONFIG_CONSTEXPR11_STD_SEARCH 1
#endif
// Control presence of exception handling (try and auto discover):
#ifndef nssv_CONFIG_NO_EXCEPTIONS
# if defined(_MSC_VER)
# include <cstddef> // for _HAS_EXCEPTIONS
# endif
# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
# define nssv_CONFIG_NO_EXCEPTIONS 0
# else
# define nssv_CONFIG_NO_EXCEPTIONS 1
# endif
#endif
// C++ language version detection (C++23 is speculative):
// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
#ifndef nssv_CPLUSPLUS
# if defined(_MSVC_LANG ) && !defined(__clang__)
# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
# else
# define nssv_CPLUSPLUS __cplusplus
# endif
#endif
#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L )
#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L )
#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L )
#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L )
#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L )
#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202002L )
#define nssv_CPP23_OR_GREATER ( nssv_CPLUSPLUS >= 202300L )
// use C++17 std::string_view if available and requested:
#if nssv_CPP17_OR_GREATER && defined(__has_include )
# if __has_include( <string_view> )
# define nssv_HAVE_STD_STRING_VIEW 1
# else
# define nssv_HAVE_STD_STRING_VIEW 0
# endif
#else
# define nssv_HAVE_STD_STRING_VIEW 0
#endif
#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) )
#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW )
#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH
//
// Use C++17 std::string_view:
//
#if nssv_USES_STD_STRING_VIEW
#include <string_view>
// Extensions for std::string:
#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
#include <string>
namespace nonstd {
template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
to_string( std::basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
{
return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
}
template< class CharT, class Traits, class Allocator >
std::basic_string_view<CharT, Traits>
to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
{
return std::basic_string_view<CharT, Traits>( s.data(), s.size() );
}
// Literal operators sv and _sv:
#if nssv_CONFIG_STD_SV_OPERATOR
using namespace std::literals::string_view_literals;
#endif
#if nssv_CONFIG_USR_SV_OPERATOR
inline namespace literals {
inline namespace string_view_literals {
constexpr std::string_view operator ""_sv( const char* str, size_t len ) noexcept // (1)
{
return std::string_view{ str, len };
}
constexpr std::u16string_view operator ""_sv( const char16_t* str, size_t len ) noexcept // (2)
{
return std::u16string_view{ str, len };
}
constexpr std::u32string_view operator ""_sv( const char32_t* str, size_t len ) noexcept // (3)
{
return std::u32string_view{ str, len };
}
constexpr std::wstring_view operator ""_sv( const wchar_t* str, size_t len ) noexcept // (4)
{
return std::wstring_view{ str, len };
}
}} // namespace literals::string_view_literals
#endif // nssv_CONFIG_USR_SV_OPERATOR
} // namespace nonstd
#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
using std::string_view;
using std::wstring_view;
using std::u16string_view;
using std::u32string_view;
using std::basic_string_view;
// literal "sv" and "_sv", see above
using std::operator==;
using std::operator!=;
using std::operator<;
using std::operator<=;
using std::operator>;
using std::operator>=;
using std::operator<<;
} // namespace nonstd
#else // nssv_HAVE_STD_STRING_VIEW
//
// Before C++17: use string_view lite:
//
// Compiler versions:
//
// MSVC++ 6.0 _MSC_VER == 1200 nssv_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0)
// MSVC++ 7.0 _MSC_VER == 1300 nssv_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002)
// MSVC++ 7.1 _MSC_VER == 1310 nssv_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003)
// MSVC++ 8.0 _MSC_VER == 1400 nssv_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005)
// MSVC++ 9.0 _MSC_VER == 1500 nssv_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008)
// MSVC++ 10.0 _MSC_VER == 1600 nssv_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010)
// MSVC++ 11.0 _MSC_VER == 1700 nssv_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012)
// MSVC++ 12.0 _MSC_VER == 1800 nssv_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013)
// MSVC++ 14.0 _MSC_VER == 1900 nssv_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015)
// MSVC++ 14.1 _MSC_VER >= 1910 nssv_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017)
// MSVC++ 14.2 _MSC_VER >= 1920 nssv_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019)
#if defined(_MSC_VER ) && !defined(__clang__)
# define nssv_COMPILER_MSVC_VER (_MSC_VER )
# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
#else
# define nssv_COMPILER_MSVC_VER 0
# define nssv_COMPILER_MSVC_VERSION 0
#endif
#define nssv_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) )
#if defined( __apple_build_version__ )
# define nssv_COMPILER_APPLECLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
# define nssv_COMPILER_CLANG_VERSION 0
#elif defined( __clang__ )
# define nssv_COMPILER_APPLECLANG_VERSION 0
# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
#else
# define nssv_COMPILER_APPLECLANG_VERSION 0
# define nssv_COMPILER_CLANG_VERSION 0
#endif
#if defined(__GNUC__) && !defined(__clang__)
# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#else
# define nssv_COMPILER_GNUC_VERSION 0
#endif
// half-open range [lo..hi):
#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
// Presence of language and library features:
#ifdef _HAS_CPP0X
# define nssv_HAS_CPP0X _HAS_CPP0X
#else
# define nssv_HAS_CPP0X 0
#endif
// Unless defined otherwise below, consider VC14 as C++11 for string-view-lite:
#if nssv_COMPILER_MSVC_VER >= 1900
# undef nssv_CPP11_OR_GREATER
# define nssv_CPP11_OR_GREATER 1
#endif
#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER)
#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER)
// Presence of C++11 language features:
#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140
#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140
#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140
#define nssv_HAVE_IS_DEFAULT nssv_CPP11_140
#define nssv_HAVE_IS_DELETE nssv_CPP11_140
#define nssv_HAVE_NOEXCEPT nssv_CPP11_140
#define nssv_HAVE_NULLPTR nssv_CPP11_100
#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140
#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140
#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
#define nssv_HAVE_WCHAR16_T nssv_CPP11_100
#define nssv_HAVE_WCHAR32_T nssv_CPP11_100
#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140
#else
# define nssv_HAVE_STD_DEFINED_LITERALS 0
#endif
// Presence of C++14 language features:
#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000
// Presence of C++17 language features:
#define nssv_HAVE_NODISCARD nssv_CPP17_000
// Presence of C++ library features:
#define nssv_HAVE_STD_HASH nssv_CPP11_120
// Presence of compiler intrinsics:
// Providing char-type specializations for compare() and length() that
// use compiler intrinsics can improve compile- and run-time performance.
//
// The challenge is in using the right combinations of builtin availability
// and its constexpr-ness.
//
// | compiler | __builtin_memcmp (constexpr) | memcmp (constexpr) |
// |----------|------------------------------|---------------------|
// | clang | 4.0 (>= 4.0 ) | any (? ) |
// | clang-a | 9.0 (>= 9.0 ) | any (? ) |
// | gcc | any (constexpr) | any (? ) |
// | msvc | >= 14.2 C++17 (>= 14.2 ) | any (? ) |
#define nssv_HAVE_BUILTIN_VER ( (nssv_CPP17_000 && nssv_COMPILER_MSVC_VERSION >= 142) || nssv_COMPILER_GNUC_VERSION > 0 || nssv_COMPILER_CLANG_VERSION >= 400 || nssv_COMPILER_APPLECLANG_VERSION >= 900 )
#define nssv_HAVE_BUILTIN_CE ( nssv_HAVE_BUILTIN_VER )
#define nssv_HAVE_BUILTIN_MEMCMP ( (nssv_HAVE_CONSTEXPR_14 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_14 )
#define nssv_HAVE_BUILTIN_STRLEN ( (nssv_HAVE_CONSTEXPR_11 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_11 )
#ifdef __has_builtin
# define nssv_HAVE_BUILTIN( x ) __has_builtin( x )
#else
# define nssv_HAVE_BUILTIN( x ) 0
#endif
#if nssv_HAVE_BUILTIN(__builtin_memcmp) || nssv_HAVE_BUILTIN_VER
# define nssv_BUILTIN_MEMCMP __builtin_memcmp
#else
# define nssv_BUILTIN_MEMCMP memcmp
#endif
#if nssv_HAVE_BUILTIN(__builtin_strlen) || nssv_HAVE_BUILTIN_VER
# define nssv_BUILTIN_STRLEN __builtin_strlen
#else
# define nssv_BUILTIN_STRLEN strlen
#endif
// C++ feature usage:
#if nssv_HAVE_CONSTEXPR_11
# define nssv_constexpr constexpr
#else
# define nssv_constexpr /*constexpr*/
#endif
#if nssv_HAVE_CONSTEXPR_14
# define nssv_constexpr14 constexpr
#else
# define nssv_constexpr14 /*constexpr*/
#endif
#if nssv_HAVE_EXPLICIT_CONVERSION
# define nssv_explicit explicit
#else
# define nssv_explicit /*explicit*/
#endif
#if nssv_HAVE_INLINE_NAMESPACE
# define nssv_inline_ns inline
#else
# define nssv_inline_ns /*inline*/
#endif
#if nssv_HAVE_NOEXCEPT
# define nssv_noexcept noexcept
#else
# define nssv_noexcept /*noexcept*/
#endif
//#if nssv_HAVE_REF_QUALIFIER
//# define nssv_ref_qual &
//# define nssv_refref_qual &&
//#else
//# define nssv_ref_qual /*&*/
//# define nssv_refref_qual /*&&*/
//#endif
#if nssv_HAVE_NULLPTR
# define nssv_nullptr nullptr
#else
# define nssv_nullptr NULL
#endif
#if nssv_HAVE_NODISCARD
# define nssv_nodiscard simdjson_warn_unused
#else
# define nssv_nodiscard /*simdjson_warn_unused*/
#endif
// Additional includes:
#include <algorithm>
#include <cassert>
#include <iterator>
#include <limits>
#include <string> // std::char_traits<>
#if ! nssv_CONFIG_NO_STREAM_INSERTION
# include <ostream>
#endif
#if ! nssv_CONFIG_NO_EXCEPTIONS
# include <stdexcept>
#endif
#if nssv_CPP11_OR_GREATER
# include <type_traits>
#endif
// Clang, GNUC, MSVC warning suppression macros:
#if defined(__clang__)
# pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wuser-defined-literals"
#elif nssv_COMPILER_GNUC_VERSION >= 480
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wliteral-suffix"
#endif // __clang__
#if nssv_COMPILER_MSVC_VERSION >= 140
# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]]
# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) )
# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes))
#else
# define nssv_SUPPRESS_MSGSL_WARNING(expr)
# define nssv_SUPPRESS_MSVC_WARNING(code, descr)
# define nssv_DISABLE_MSVC_WARNINGS(codes)
#endif
#if defined(__clang__)
# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop")
#elif nssv_COMPILER_GNUC_VERSION >= 480
# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop")
#elif nssv_COMPILER_MSVC_VERSION >= 140
# define nssv_RESTORE_WARNINGS() __pragma(warning(pop ))
#else
# define nssv_RESTORE_WARNINGS()
#endif
// Suppress the following MSVC (GSL) warnings:
// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not
// start with an underscore are reserved
// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
// use brace initialization, gsl::narrow_cast or gsl::narow
// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 )
//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
namespace nonstd { namespace sv_lite {
//
// basic_string_view declaration:
//
template
<
class CharT,
class Traits = std::char_traits<CharT>
>
class basic_string_view;
namespace detail {
// support constexpr comparison in C++14;
// for C++17 and later, use provided traits:
template< typename CharT >
inline nssv_constexpr14 int compare( CharT const * s1, CharT const * s2, std::size_t count )
{
while ( count-- != 0 )
{
if ( *s1 < *s2 ) return -1;
if ( *s1 > *s2 ) return +1;
++s1; ++s2;
}
return 0;
}
#if nssv_HAVE_BUILTIN_MEMCMP
// specialization of compare() for char, see also generic compare() above:
inline nssv_constexpr14 int compare( char const * s1, char const * s2, std::size_t count )
{
return nssv_BUILTIN_MEMCMP( s1, s2, count );
}
#endif
#if nssv_HAVE_BUILTIN_STRLEN
// specialization of length() for char, see also generic length() further below:
inline nssv_constexpr std::size_t length( char const * s )
{
return nssv_BUILTIN_STRLEN( s );
}
#endif
#if defined(__OPTIMIZE__)
// gcc, clang provide __OPTIMIZE__
// Expect tail call optimization to make length() non-recursive:
template< typename CharT >
inline nssv_constexpr std::size_t length( CharT * s, std::size_t result = 0 )
{
return *s == '\0' ? result : length( s + 1, result + 1 );
}
#else // OPTIMIZE
// non-recursive:
template< typename CharT >
inline nssv_constexpr14 std::size_t length( CharT * s )
{
std::size_t result = 0;
while ( *s++ != '\0' )
{
++result;
}
return result;
}
#endif // OPTIMIZE
#if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER
#if defined(__OPTIMIZE__)
// gcc, clang provide __OPTIMIZE__
// Expect tail call optimization to make search() non-recursive:
template< class CharT, class Traits = std::char_traits<CharT> >
constexpr const CharT* search( basic_string_view<CharT, Traits> haystack, basic_string_view<CharT, Traits> needle )
{
return haystack.starts_with( needle ) ? haystack.begin() :
haystack.empty() ? haystack.end() : search( haystack.substr(1), needle );
}
#else // OPTIMIZE
// non-recursive:
#if nssv_CONFIG_CONSTEXPR11_STD_SEARCH
template< class CharT, class Traits = std::char_traits<CharT> >
constexpr const CharT* search( basic_string_view<CharT, Traits> haystack, basic_string_view<CharT, Traits> needle )
{
return std::search( haystack.begin(), haystack.end(), needle.begin(), needle.end() );
}
#else // nssv_CONFIG_CONSTEXPR11_STD_SEARCH
template< class CharT, class Traits = std::char_traits<CharT> >
nssv_constexpr14 const CharT* search( basic_string_view<CharT, Traits> haystack, basic_string_view<CharT, Traits> needle )
{
while ( needle.size() <= haystack.size() )
{
if ( haystack.starts_with(needle) )
{
return haystack.cbegin();
}
haystack = basic_string_view<CharT, Traits>{ haystack.begin() + 1, haystack.size() - 1U };
}
return haystack.cend();
}
#endif // nssv_CONFIG_CONSTEXPR11_STD_SEARCH
#endif // OPTIMIZE
#endif // nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER
} // namespace detail
//
// basic_string_view:
//
template
<
class CharT,
class Traits /* = std::char_traits<CharT> */
>
class basic_string_view
{
public:
// Member types:
typedef Traits traits_type;
typedef CharT value_type;
typedef CharT * pointer;
typedef CharT const * const_pointer;
typedef CharT & reference;
typedef CharT const & const_reference;
typedef const_pointer iterator;
typedef const_pointer const_iterator;
typedef std::reverse_iterator< const_iterator > reverse_iterator;
typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
// 24.4.2.1 Construction and assignment:
nssv_constexpr basic_string_view() nssv_noexcept
: data_( nssv_nullptr )
, size_( 0 )
{}
#if nssv_CPP11_OR_GREATER
nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default;
#else
nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept
: data_( other.data_)
, size_( other.size_)
{}
#endif
nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept
: data_( s )
, size_( count )
{}
nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept
: data_( s )
#if nssv_CPP17_OR_GREATER
, size_( Traits::length(s) )
#elif nssv_CPP11_OR_GREATER
, size_( detail::length(s) )
#else
, size_( Traits::length(s) )
#endif
{}
#if nssv_HAVE_NULLPTR
# if nssv_HAVE_IS_DELETE
nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept = delete;
# else
private: nssv_constexpr basic_string_view( std::nullptr_t ) nssv_noexcept; public:
# endif
#endif
// Assignment:
#if nssv_CPP11_OR_GREATER
nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default;
#else
nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept
{
data_ = other.data_;
size_ = other.size_;
return *this;
}
#endif
// 24.4.2.2 Iterator support:
nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; }
nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; }
nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); }
nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); }
nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); }
nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); }
// 24.4.2.3 Capacity:
nssv_constexpr size_type size() const nssv_noexcept { return size_; }
nssv_constexpr size_type length() const nssv_noexcept { return size_; }
nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); }
// since C++20
nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept
{
return 0 == size_;
}
// 24.4.2.4 Element access:
nssv_constexpr const_reference operator[]( size_type pos ) const
{
return data_at( pos );
}
nssv_constexpr14 const_reference at( size_type pos ) const
{
#if nssv_CONFIG_NO_EXCEPTIONS
assert( pos < size() );
#else
if ( pos >= size() )
{
throw std::out_of_range("nonstd::string_view::at()");
}
#endif
return data_at( pos );
}
nssv_constexpr const_reference front() const { return data_at( 0 ); }
nssv_constexpr const_reference back() const { return data_at( size() - 1 ); }
nssv_constexpr const_pointer data() const nssv_noexcept { return data_; }
// 24.4.2.5 Modifiers:
nssv_constexpr14 void remove_prefix( size_type n )
{
assert( n <= size() );
data_ += n;
size_ -= n;
}
nssv_constexpr14 void remove_suffix( size_type n )
{
assert( n <= size() );
size_ -= n;
}
nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept
{
const basic_string_view tmp(other);
other = *this;
*this = tmp;
}
// 24.4.2.6 String operations:
size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const
{
#if nssv_CONFIG_NO_EXCEPTIONS
assert( pos <= size() );
#else
if ( pos > size() )
{
throw std::out_of_range("nonstd::string_view::copy()");
}
#endif
const size_type rlen = (std::min)( n, size() - pos );
(void) Traits::copy( dest, data() + pos, rlen );
return rlen;
}
nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const
{
#if nssv_CONFIG_NO_EXCEPTIONS
assert( pos <= size() );
#else
if ( pos > size() )
{
throw std::out_of_range("nonstd::string_view::substr()");
}
#endif
return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
}
// compare(), 6x:
nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
{
#if nssv_CPP17_OR_GREATER
if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
#else
if ( const int result = detail::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
#endif
{
return result;
}
return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
}
nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2)
{
return substr( pos1, n1 ).compare( other );
}
nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3)
{
return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) );
}
nssv_constexpr int compare( CharT const * s ) const // (4)
{
return compare( basic_string_view( s ) );
}
nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5)
{
return substr( pos1, n1 ).compare( basic_string_view( s ) );
}
nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6)
{
return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) );
}
// 24.4.2.7 Searching:
// starts_with(), 3x, since C++20:
nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1)
{
return size() >= v.size() && compare( 0, v.size(), v ) == 0;
}
nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2)
{
return starts_with( basic_string_view( &c, 1 ) );
}
nssv_constexpr bool starts_with( CharT const * s ) const // (3)
{
return starts_with( basic_string_view( s ) );
}
// ends_with(), 3x, since C++20:
nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1)
{
return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0;
}
nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2)
{
return ends_with( basic_string_view( &c, 1 ) );
}
nssv_constexpr bool ends_with( CharT const * s ) const // (3)
{
return ends_with( basic_string_view( s ) );
}
// find(), 4x:
nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
{
return assert( v.size() == 0 || v.data() != nssv_nullptr )
, pos >= size()
? npos : to_pos(
#if nssv_CPP11_OR_GREATER && ! nssv_CPP17_OR_GREATER
detail::search( substr(pos), v )
#else
std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq )
#endif
);
}
nssv_constexpr size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
{
return find( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find( CharT const * s, size_type pos, size_type n ) const // (3)
{
return find( basic_string_view( s, n ), pos );
}
nssv_constexpr size_type find( CharT const * s, size_type pos = 0 ) const // (4)
{
return find( basic_string_view( s ), pos );
}
// rfind(), 4x:
nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
{
if ( size() < v.size() )
{
return npos;
}
if ( v.empty() )
{
return (std::min)( size(), pos );
}
const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
return result != last ? size_type( result - cbegin() ) : npos;
}
nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
{
return rfind( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3)
{
return rfind( basic_string_view( s, n ), pos );
}
nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4)
{
return rfind( basic_string_view( s ), pos );
}
// find_first_of(), 4x:
nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
{
return pos >= size()
? npos
: to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
}
nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
{
return find_first_of( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3)
{
return find_first_of( basic_string_view( s, n ), pos );
}
nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4)
{
return find_first_of( basic_string_view( s ), pos );
}
// find_last_of(), 4x:
nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
{
return empty()
? npos
: pos >= size()
? find_last_of( v, size() - 1 )
: to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) );
}
nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
{
return find_last_of( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3)
{
return find_last_of( basic_string_view( s, count ), pos );
}
nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4)
{
return find_last_of( basic_string_view( s ), pos );
}
// find_first_not_of(), 4x:
nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1)
{
return pos >= size()
? npos
: to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) );
}
nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2)
{
return find_first_not_of( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3)
{
return find_first_not_of( basic_string_view( s, count ), pos );
}
nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4)
{
return find_first_not_of( basic_string_view( s ), pos );
}
// find_last_not_of(), 4x:
nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1)
{
return empty()
? npos
: pos >= size()
? find_last_not_of( v, size() - 1 )
: to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) );
}
nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2)
{
return find_last_not_of( basic_string_view( &c, 1 ), pos );
}
nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3)
{
return find_last_not_of( basic_string_view( s, count ), pos );
}
nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4)
{
return find_last_not_of( basic_string_view( s ), pos );
}
// Constants:
#if nssv_CPP17_OR_GREATER
static nssv_constexpr size_type npos = size_type(-1);
#elif nssv_CPP11_OR_GREATER
enum : size_type { npos = size_type(-1) };
#else
enum { npos = size_type(-1) };
#endif
private:
struct not_in_view
{
const basic_string_view v;
nssv_constexpr explicit not_in_view( basic_string_view v_ ) : v( v_ ) {}
nssv_constexpr bool operator()( CharT c ) const
{
return npos == v.find_first_of( c );
}
};
nssv_constexpr size_type to_pos( const_iterator it ) const
{
return it == cend() ? npos : size_type( it - cbegin() );
}
nssv_constexpr size_type to_pos( const_reverse_iterator it ) const
{
return it == crend() ? npos : size_type( crend() - it - 1 );
}
nssv_constexpr const_reference data_at( size_type pos ) const
{
#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 )
return data_[pos];
#else
return assert( pos < size() ), data_[pos];
#endif
}
private:
const_pointer data_;
size_type size_;
public:
#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
template< class Allocator >
basic_string_view( std::basic_string<CharT, Traits, Allocator> const & s ) nssv_noexcept
: data_( s.data() )
, size_( s.size() )
{}
#if nssv_HAVE_EXPLICIT_CONVERSION
template< class Allocator >
explicit operator std::basic_string<CharT, Traits, Allocator>() const
{
return to_string( Allocator() );
}
#endif // nssv_HAVE_EXPLICIT_CONVERSION
#if nssv_CPP11_OR_GREATER
template< class Allocator = std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
to_string( Allocator const & a = Allocator() ) const
{
return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
}
#else
std::basic_string<CharT, Traits>
to_string() const
{
return std::basic_string<CharT, Traits>( begin(), end() );
}
template< class Allocator >
std::basic_string<CharT, Traits, Allocator>
to_string( Allocator const & a ) const
{
return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
}
#endif // nssv_CPP11_OR_GREATER
#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
};
//
// Non-member functions:
//
// 24.4.3 Non-member comparison functions:
// lexicographically compare two string views (function template):
template< class CharT, class Traits >
nssv_constexpr bool operator== (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
template< class CharT, class Traits >
nssv_constexpr bool operator!= (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return !( lhs == rhs ); }
template< class CharT, class Traits >
nssv_constexpr bool operator< (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) < 0; }
template< class CharT, class Traits >
nssv_constexpr bool operator<= (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) <= 0; }
template< class CharT, class Traits >
nssv_constexpr bool operator> (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) > 0; }
template< class CharT, class Traits >
nssv_constexpr bool operator>= (
basic_string_view <CharT, Traits> lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) >= 0; }
// Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
// Implementations shall provide sufficient additional overloads marked
// constexpr and noexcept so that an object t with an implicit conversion
// to S can be compared according to Table 67.
#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
// accommodate for older compilers:
// ==
template< class CharT, class Traits>
nssv_constexpr bool operator==(
basic_string_view<CharT, Traits> lhs,
CharT const * rhs ) nssv_noexcept
{ return lhs.size() == detail::length( rhs ) && lhs.compare( rhs ) == 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator==(
CharT const * lhs,
basic_string_view<CharT, Traits> rhs ) nssv_noexcept
{ return detail::length( lhs ) == rhs.size() && rhs.compare( lhs ) == 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator==(
basic_string_view<CharT, Traits> lhs,
std::basic_string<CharT, Traits> rhs ) nssv_noexcept
{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator==(
std::basic_string<CharT, Traits> rhs,
basic_string_view<CharT, Traits> lhs ) nssv_noexcept
{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
// !=
template< class CharT, class Traits>
nssv_constexpr bool operator!=(
basic_string_view<CharT, Traits> lhs,
CharT const * rhs ) nssv_noexcept
{ return !( lhs == rhs ); }
template< class CharT, class Traits>
nssv_constexpr bool operator!=(
CharT const * lhs,
basic_string_view<CharT, Traits> rhs ) nssv_noexcept
{ return !( lhs == rhs ); }
template< class CharT, class Traits>
nssv_constexpr bool operator!=(
basic_string_view<CharT, Traits> lhs,
std::basic_string<CharT, Traits> rhs ) nssv_noexcept
{ return !( lhs == rhs ); }
template< class CharT, class Traits>
nssv_constexpr bool operator!=(
std::basic_string<CharT, Traits> rhs,
basic_string_view<CharT, Traits> lhs ) nssv_noexcept
{ return !( lhs == rhs ); }
// <
template< class CharT, class Traits>
nssv_constexpr bool operator<(
basic_string_view<CharT, Traits> lhs,
CharT const * rhs ) nssv_noexcept
{ return lhs.compare( rhs ) < 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator<(
CharT const * lhs,
basic_string_view<CharT, Traits> rhs ) nssv_noexcept
{ return rhs.compare( lhs ) > 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator<(
basic_string_view<CharT, Traits> lhs,
std::basic_string<CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) < 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator<(
std::basic_string<CharT, Traits> rhs,
basic_string_view<CharT, Traits> lhs ) nssv_noexcept
{ return rhs.compare( lhs ) > 0; }
// <=
template< class CharT, class Traits>
nssv_constexpr bool operator<=(
basic_string_view<CharT, Traits> lhs,
CharT const * rhs ) nssv_noexcept
{ return lhs.compare( rhs ) <= 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator<=(
CharT const * lhs,
basic_string_view<CharT, Traits> rhs ) nssv_noexcept
{ return rhs.compare( lhs ) >= 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator<=(
basic_string_view<CharT, Traits> lhs,
std::basic_string<CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) <= 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator<=(
std::basic_string<CharT, Traits> rhs,
basic_string_view<CharT, Traits> lhs ) nssv_noexcept
{ return rhs.compare( lhs ) >= 0; }
// >
template< class CharT, class Traits>
nssv_constexpr bool operator>(
basic_string_view<CharT, Traits> lhs,
CharT const * rhs ) nssv_noexcept
{ return lhs.compare( rhs ) > 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator>(
CharT const * lhs,
basic_string_view<CharT, Traits> rhs ) nssv_noexcept
{ return rhs.compare( lhs ) < 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator>(
basic_string_view<CharT, Traits> lhs,
std::basic_string<CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) > 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator>(
std::basic_string<CharT, Traits> rhs,
basic_string_view<CharT, Traits> lhs ) nssv_noexcept
{ return rhs.compare( lhs ) < 0; }
// >=
template< class CharT, class Traits>
nssv_constexpr bool operator>=(
basic_string_view<CharT, Traits> lhs,
CharT const * rhs ) nssv_noexcept
{ return lhs.compare( rhs ) >= 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator>=(
CharT const * lhs,
basic_string_view<CharT, Traits> rhs ) nssv_noexcept
{ return rhs.compare( lhs ) <= 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator>=(
basic_string_view<CharT, Traits> lhs,
std::basic_string<CharT, Traits> rhs ) nssv_noexcept
{ return lhs.compare( rhs ) >= 0; }
template< class CharT, class Traits>
nssv_constexpr bool operator>=(
std::basic_string<CharT, Traits> rhs,
basic_string_view<CharT, Traits> lhs ) nssv_noexcept
{ return rhs.compare( lhs ) <= 0; }
#else // newer compilers:
#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view<T,U> >::type
#if defined(_MSC_VER) // issue 40
# define nssv_MSVC_ORDER(x) , int=x
#else
# define nssv_MSVC_ORDER(x) /*, int=x*/
#endif
// ==
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator==(
basic_string_view <CharT, Traits> lhs,
nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept
{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator==(
nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
basic_string_view <CharT, Traits> rhs ) nssv_noexcept
{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
// !=
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator!= (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return !( lhs == rhs ); }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator!= (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return !( lhs == rhs ); }
// <
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator< (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) < 0; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator< (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) < 0; }
// <=
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator<= (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) <= 0; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator<= (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) <= 0; }
// >
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator> (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) > 0; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator> (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) > 0; }
// >=
template< class CharT, class Traits nssv_MSVC_ORDER(1) >
nssv_constexpr bool operator>= (
basic_string_view < CharT, Traits > lhs,
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
{ return lhs.compare( rhs ) >= 0; }
template< class CharT, class Traits nssv_MSVC_ORDER(2) >
nssv_constexpr bool operator>= (
nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
basic_string_view < CharT, Traits > rhs ) nssv_noexcept
{ return lhs.compare( rhs ) >= 0; }
#undef nssv_MSVC_ORDER
#undef nssv_BASIC_STRING_VIEW_I
#endif // compiler-dependent approach to comparisons
// 24.4.4 Inserters and extractors:
#if ! nssv_CONFIG_NO_STREAM_INSERTION
namespace detail {
template< class Stream >
void write_padding( Stream & os, std::streamsize n )
{
for ( std::streamsize i = 0; i < n; ++i )
os.rdbuf()->sputc( os.fill() );
}
template< class Stream, class View >
Stream & write_to_stream( Stream & os, View const & sv )
{
typename Stream::sentry sentry( os );
if ( !sentry )
return os;
const std::streamsize length = static_cast<std::streamsize>( sv.length() );
// Whether, and how, to pad:
const bool pad = ( length < os.width() );
const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right;
if ( left_pad )
write_padding( os, os.width() - length );
// Write span characters:
os.rdbuf()->sputn( sv.begin(), length );
if ( pad && !left_pad )
write_padding( os, os.width() - length );
// Reset output stream width:
os.width( 0 );
return os;
}
} // namespace detail
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits> &
operator<<(
std::basic_ostream<CharT, Traits>& os,
basic_string_view <CharT, Traits> sv )
{
return detail::write_to_stream( os, sv );
}
#endif // nssv_CONFIG_NO_STREAM_INSERTION
// Several typedefs for common character types are provided:
typedef basic_string_view<char> string_view;
typedef basic_string_view<wchar_t> wstring_view;
#if nssv_HAVE_WCHAR16_T
typedef basic_string_view<char16_t> u16string_view;
typedef basic_string_view<char32_t> u32string_view;
#endif
}} // namespace nonstd::sv_lite
//
// 24.4.6 Suffix for basic_string_view literals:
//
#if nssv_HAVE_USER_DEFINED_LITERALS
namespace nonstd {
nssv_inline_ns namespace literals {
nssv_inline_ns namespace string_view_literals {
#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
nssv_constexpr nonstd::sv_lite::string_view operator ""sv( const char* str, size_t len ) nssv_noexcept // (1)
{
return nonstd::sv_lite::string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::u16string_view operator ""sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{
return nonstd::sv_lite::u16string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::u32string_view operator ""sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{
return nonstd::sv_lite::u32string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::wstring_view operator ""sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{
return nonstd::sv_lite::wstring_view{ str, len };
}
#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
#if nssv_CONFIG_USR_SV_OPERATOR
nssv_constexpr nonstd::sv_lite::string_view operator ""_sv( const char* str, size_t len ) nssv_noexcept // (1)
{
return nonstd::sv_lite::string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::u16string_view operator ""_sv( const char16_t* str, size_t len ) nssv_noexcept // (2)
{
return nonstd::sv_lite::u16string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::u32string_view operator ""_sv( const char32_t* str, size_t len ) nssv_noexcept // (3)
{
return nonstd::sv_lite::u32string_view{ str, len };
}
nssv_constexpr nonstd::sv_lite::wstring_view operator ""_sv( const wchar_t* str, size_t len ) nssv_noexcept // (4)
{
return nonstd::sv_lite::wstring_view{ str, len };
}
#endif // nssv_CONFIG_USR_SV_OPERATOR
}}} // namespace nonstd::literals::string_view_literals
#endif
//
// Extensions for std::string:
//
#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
namespace nonstd {
namespace sv_lite {
// Exclude MSVC 14 (19.00): it yields ambiguous to_string():
#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
std::basic_string<CharT, Traits, Allocator>
to_string( basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
{
return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
}
#else
template< class CharT, class Traits >
std::basic_string<CharT, Traits>
to_string( basic_string_view<CharT, Traits> v )
{
return std::basic_string<CharT, Traits>( v.begin(), v.end() );
}
template< class CharT, class Traits, class Allocator >
std::basic_string<CharT, Traits, Allocator>
to_string( basic_string_view<CharT, Traits> v, Allocator const & a )
{
return std::basic_string<CharT, Traits, Allocator>( v.begin(), v.end(), a );
}
#endif // nssv_CPP11_OR_GREATER
template< class CharT, class Traits, class Allocator >
basic_string_view<CharT, Traits>
to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
{
return basic_string_view<CharT, Traits>( s.data(), s.size() );
}
}} // namespace nonstd::sv_lite
#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
//
// make types and algorithms available in namespace nonstd:
//
namespace nonstd {
using sv_lite::basic_string_view;
using sv_lite::string_view;
using sv_lite::wstring_view;
#if nssv_HAVE_WCHAR16_T
using sv_lite::u16string_view;
#endif
#if nssv_HAVE_WCHAR32_T
using sv_lite::u32string_view;
#endif
// literal "sv"
using sv_lite::operator==;
using sv_lite::operator!=;
using sv_lite::operator<;
using sv_lite::operator<=;
using sv_lite::operator>;
using sv_lite::operator>=;
#if ! nssv_CONFIG_NO_STREAM_INSERTION
using sv_lite::operator<<;
#endif
#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
using sv_lite::to_string;
using sv_lite::to_string_view;
#endif
} // namespace nonstd
// 24.4.5 Hash support (C++11):
// Note: The hash value of a string view object is equal to the hash value of
// the corresponding string object.
#if nssv_HAVE_STD_HASH
#include <functional>
namespace std {
template<>
struct hash< nonstd::string_view >
{
public:
std::size_t operator()( nonstd::string_view v ) const nssv_noexcept
{
return std::hash<std::string>()( std::string( v.data(), v.size() ) );
}
};
template<>
struct hash< nonstd::wstring_view >
{
public:
std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept
{
return std::hash<std::wstring>()( std::wstring( v.data(), v.size() ) );
}
};
template<>
struct hash< nonstd::u16string_view >
{
public:
std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept
{
return std::hash<std::u16string>()( std::u16string( v.data(), v.size() ) );
}
};
template<>
struct hash< nonstd::u32string_view >
{
public:
std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept
{
return std::hash<std::u32string>()( std::u32string( v.data(), v.size() ) );
}
};
} // namespace std
#endif // nssv_HAVE_STD_HASH
nssv_RESTORE_WARNINGS()
#endif // nssv_HAVE_STD_STRING_VIEW
#endif // NONSTD_SV_LITE_H_INCLUDED
/* end file simdjson/nonstd/string_view.hpp */
SIMDJSON_POP_DISABLE_WARNINGS
namespace std {
using string_view = nonstd::string_view;
}
#endif // SIMDJSON_HAS_STRING_VIEW
#undef SIMDJSON_HAS_STRING_VIEW // We are not going to need this macro anymore.
/// If EXPR is an error, returns it.
#define SIMDJSON_TRY(EXPR) { auto _err = (EXPR); if (_err) { return _err; } }
// Unless the programmer has already set SIMDJSON_DEVELOPMENT_CHECKS,
// we want to set it under debug builds. We detect a debug build
// under Visual Studio when the _DEBUG macro is set. Under the other
// compilers, we use the fact that they define __OPTIMIZE__ whenever
// they allow optimizations.
// It is possible that this could miss some cases where SIMDJSON_DEVELOPMENT_CHECKS
// is helpful, but the programmer can set the macro SIMDJSON_DEVELOPMENT_CHECKS.
// It could also wrongly set SIMDJSON_DEVELOPMENT_CHECKS (e.g., if the programmer
// sets _DEBUG in a release build under Visual Studio, or if some compiler fails to
// set the __OPTIMIZE__ macro).
// We make it so that if NDEBUG is defined, then SIMDJSON_DEVELOPMENT_CHECKS
// is not defined, irrespective of the compiler.
// We recommend that users set NDEBUG in release builds, so that
// SIMDJSON_DEVELOPMENT_CHECKS is not defined in release builds by default,
// irrespective of the compiler.
#ifndef SIMDJSON_DEVELOPMENT_CHECKS
#ifdef _MSC_VER
// Visual Studio seems to set _DEBUG for debug builds.
// We set SIMDJSON_DEVELOPMENT_CHECKS to 1 if _DEBUG is defined
// and NDEBUG is not defined.
#if defined(_DEBUG) && !defined(NDEBUG)
#define SIMDJSON_DEVELOPMENT_CHECKS 1
#endif // _DEBUG
#else // _MSC_VER
// All other compilers appear to set __OPTIMIZE__ to a positive integer
// when the compiler is optimizing.
// We only set SIMDJSON_DEVELOPMENT_CHECKS if both __OPTIMIZE__
// and NDEBUG are not defined.
// We recognize _DEBUG as overriding __OPTIMIZE__ so that if both
// __OPTIMIZE__ and _DEBUG are defined, we still set SIMDJSON_DEVELOPMENT_CHECKS.
#if ((!defined(__OPTIMIZE__) || defined(_DEBUG)) && !defined(NDEBUG))
#define SIMDJSON_DEVELOPMENT_CHECKS 1
#endif // __OPTIMIZE__
#endif // _MSC_VER
#endif // SIMDJSON_DEVELOPMENT_CHECKS
// The SIMDJSON_CHECK_EOF macro is a feature flag for the "don't require padding"
// feature.
#if SIMDJSON_CPLUSPLUS17
// if we have C++, then fallthrough is a default attribute
# define simdjson_fallthrough [[fallthrough]]
// check if we have __attribute__ support
#elif defined(__has_attribute)
// check if we have the __fallthrough__ attribute
#if __has_attribute(__fallthrough__)
// we are good to go:
# define simdjson_fallthrough __attribute__((__fallthrough__))
#endif // __has_attribute(__fallthrough__)
#endif // SIMDJSON_CPLUSPLUS17
// on some systems, we simply do not have support for fallthrough, so use a default:
#ifndef simdjson_fallthrough
# define simdjson_fallthrough do {} while (0) /* fallthrough */
#endif // simdjson_fallthrough
#if SIMDJSON_DEVELOPMENT_CHECKS
#define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { assert ((expr)); } while (0)
#else
#define SIMDJSON_DEVELOPMENT_ASSERT(expr) do { } while (0)
#endif
#ifndef SIMDJSON_UTF8VALIDATION
#define SIMDJSON_UTF8VALIDATION 1
#endif
#ifdef __has_include
// How do we detect that a compiler supports vbmi2?
// For sure if the following header is found, we are ok?
#if __has_include(<avx512vbmi2intrin.h>)
#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1
#endif
#endif
#ifdef _MSC_VER
#if _MSC_VER >= 1920
// Visual Studio 2019 and up support VBMI2 under x64 even if the header
// avx512vbmi2intrin.h is not found.
#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1
#endif
#endif
// By default, we allow AVX512.
#ifndef SIMDJSON_AVX512_ALLOWED
#define SIMDJSON_AVX512_ALLOWED 1
#endif
#ifndef __has_cpp_attribute
#define simdjson_lifetime_bound
#elif __has_cpp_attribute(msvc::lifetimebound)
#define simdjson_lifetime_bound [[msvc::lifetimebound]]
#elif __has_cpp_attribute(clang::lifetimebound)
#define simdjson_lifetime_bound [[clang::lifetimebound]]
#elif __has_cpp_attribute(lifetimebound)
#define simdjson_lifetime_bound [[lifetimebound]]
#else
#define simdjson_lifetime_bound
#endif
#endif // SIMDJSON_COMMON_DEFS_H
/* end file simdjson/common_defs.h */
// This provides the public API for simdjson.
// DOM and ondemand are amalgamated separately, in simdjson.h
/* including simdjson/simdjson_version.h: #include "simdjson/simdjson_version.h" */
/* begin file simdjson/simdjson_version.h */
// /include/simdjson/simdjson_version.h automatically generated by release.py,
// do not change by hand
#ifndef SIMDJSON_SIMDJSON_VERSION_H
#define SIMDJSON_SIMDJSON_VERSION_H
/** The version of simdjson being used (major.minor.revision) */
#define SIMDJSON_VERSION "5.0.4"
namespace simdjson {
enum {
/**
* The major version (MAJOR.minor.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MAJOR = 5,
/**
* The minor version (major.MINOR.revision) of simdjson being used.
*/
SIMDJSON_VERSION_MINOR = 0,
/**
* The revision (major.minor.REVISION) of simdjson being used.
*/
SIMDJSON_VERSION_REVISION = 4
};
} // namespace simdjson
#endif // SIMDJSON_SIMDJSON_VERSION_H
/* end file simdjson/simdjson_version.h */
/* including simdjson/base.h: #include "simdjson/base.h" */
/* begin file simdjson/base.h */
/**
* @file Base declarations for all simdjson headers
* @private
*/
#ifndef SIMDJSON_BASE_H
#define SIMDJSON_BASE_H
/* skipped duplicate #include "simdjson/common_defs.h" */
/* skipped duplicate #include "simdjson/compiler_check.h" */
/* including simdjson/error.h: #include "simdjson/error.h" */
/* begin file simdjson/error.h */
#ifndef SIMDJSON_ERROR_H
#define SIMDJSON_ERROR_H
/* skipped duplicate #include "simdjson/base.h" */
#include <string>
#include <ostream>
namespace simdjson {
/**
* All possible errors returned by simdjson. These error codes are subject to change
* and not all simdjson kernel returns the same error code given the same input: it is not
* well defined which error a given input should produce.
*
* Only SUCCESS evaluates to false as a Boolean. All other error codes will evaluate
* to true as a Boolean.
*/
enum error_code {
SUCCESS = 0, ///< No error
CAPACITY, ///< This parser can't support a document that big
MEMALLOC, ///< Error allocating memory, most likely out of memory
TAPE_ERROR, ///< Something went wrong, this is a generic error. Fatal/unrecoverable error.
DEPTH_ERROR, ///< Your document exceeds the user-specified depth limitation
STRING_ERROR, ///< Problem while parsing a string
T_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 't'
F_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 'f'
N_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 'n'
NUMBER_ERROR, ///< Problem while parsing a number
BIGINT_ERROR, ///< The integer value exceeds 64 bits
UTF8_ERROR, ///< the input is not valid UTF-8
UNINITIALIZED, ///< unknown error, or uninitialized document
EMPTY, ///< no structural element found
UNESCAPED_CHARS, ///< found unescaped characters in a string.
UNCLOSED_STRING, ///< missing quote at the end
UNSUPPORTED_ARCHITECTURE, ///< unsupported architecture
INCORRECT_TYPE, ///< JSON element has a different type than user expected
NUMBER_OUT_OF_RANGE, ///< JSON number does not fit in 64 bits
INDEX_OUT_OF_BOUNDS, ///< JSON array index too large
NO_SUCH_FIELD, ///< JSON field not found in object
IO_ERROR, ///< Error reading a file
INVALID_JSON_POINTER, ///< Invalid JSON pointer syntax
INVALID_URI_FRAGMENT, ///< Invalid URI fragment
UNEXPECTED_ERROR, ///< indicative of a bug in simdjson
PARSER_IN_USE, ///< parser is already in use.
OUT_OF_ORDER_ITERATION, ///< tried to iterate an array or object out of order (checked when SIMDJSON_DEVELOPMENT_CHECKS=1)
INSUFFICIENT_PADDING, ///< The JSON doesn't have enough padding for simdjson to safely parse it.
INCOMPLETE_ARRAY_OR_OBJECT, ///< The document ends early. Fatal/unrecoverable error.
SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value.
OUT_OF_BOUNDS, ///< Attempted to access location outside of document.
TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input
OUT_OF_CAPACITY, ///< The capacity was exceeded, we cannot allocate enough memory.
UNKNOWN_FIELD, ///< JSON field does not map to any member of the target (see simdjson::deny_unknown_fields)
NUM_ERROR_CODES ///< Placeholder for end of error code list.
};
/**
* Some errors are fatal and invalidate the document. This function returns true if the
* error is fatal. It returns true for TAPE_ERROR and INCOMPLETE_ARRAY_OR_OBJECT.
* Once a fatal error is encountered, the on-demand document is no longer valid and
* processing should stop.
*/
inline bool is_fatal(error_code error) noexcept;
/**
* It is the convention throughout the code that the macro SIMDJSON_DEVELOPMENT_CHECKS determines whether
* we check for OUT_OF_ORDER_ITERATION. The logic behind it is that these errors only occurs when the code
* that was written while breaking some simdjson::ondemand requirement. They should not occur in released
* code after these issues were fixed.
*/
/**
* Get the error message for the given error code.
*
* dom::parser parser;
* dom::element doc;
* auto error = parser.parse("foo",3).get(doc);
* if (error) { printf("Error: %s\n", error_message(error)); }
*
* @return The error message.
*/
inline const char *error_message(error_code error) noexcept;
/**
* Write the error message to the output stream
*/
inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept;
/**
* Exception thrown when an exception-supporting simdjson method is called
*/
struct simdjson_error : public std::exception {
/**
* Create an exception from a simdjson error code.
* @param error The error code
*/
simdjson_error(error_code error) noexcept : _error{error} { }
/** The error message */
const char *what() const noexcept override { return error_message(error()); }
/** The error code */
error_code error() const noexcept { return _error; }
private:
/** The error code that was used */
error_code _error;
};
namespace internal {
/**
* The result of a simdjson operation that could fail.
*
* IMPORTANT: For the ondemand API, we use implementation_simdjson_result_base<T> as a base class
* to avoid some compilation issue. Thus, if you modify this class, please ensure that the ondemand
* implementation_simdjson_result_base<T> is also modified.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::simdjson_result_base<T> {
* simdjson_result() noexcept : internal::simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct simdjson_result_base : protected std::pair<T, error_code> {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline simdjson_result_base() noexcept;
/**
* Create a new error result.
*/
simdjson_inline simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Dereference operator to access the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
* We discourage the use of value_unsafe().
*
* The recommended pattern is:
*
* T value; // where T is the type
* auto error = result.get(value);
* if (error) {
* // handle error
* }
*
* Or you may call 'value()' which will raise an exception
* in case of error:
*
* T value = result.value();
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
* We discourage the use of value_unsafe().
*
* The recommended pattern is:
*
* T value; // where T is the type
* auto error = result.get(value);
* if (error) {
* // handle error, return, exit, abort
* } else {
* // use value here.
* }
*
* Or you may call 'value()' which will raise an exception
* in case of error:
*
* T value = result.value();
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
}; // struct simdjson_result_base
} // namespace internal
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*/
template<typename T>
struct simdjson_result : public internal::simdjson_result_base<T> {
/**
* @private Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline simdjson_result() noexcept;
/**
* @private Create a new successful result.
*/
simdjson_inline simdjson_result(T &&value) noexcept;
/**
* @private Create a new error result.
*/
simdjson_inline simdjson_result(error_code error_code) noexcept;
/**
* @private Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline simdjson_result(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* Copy the value to a provided std::string, only enabled for std::string_view.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
template <typename U = T>
simdjson_warn_unused simdjson_inline error_code get(std::string &value) && noexcept {
static_assert(std::is_same<U, std::string_view>::value, "SFINAE");
std::string_view v;
error_code error = std::forward<simdjson_result<T>>(*this).get(v);
if (!error) {
value.assign(v.data(), v.size());
}
return error;
}
/**
* The error.
*/
simdjson_inline error_code error() const noexcept;
#if SIMDJSON_EXCEPTIONS
using internal::simdjson_result_base<T>::operator*;
using internal::simdjson_result_base<T>::operator->;
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
}; // struct simdjson_result
#if SIMDJSON_EXCEPTIONS
template<typename T>
inline std::ostream& operator<<(std::ostream& out, simdjson_result<T> value) { return out << value.value(); }
#endif // SIMDJSON_EXCEPTIONS
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated This is an alias and will be removed, use error_code instead
*/
using ErrorValues [[deprecated("This is an alias and will be removed, use error_code instead")]] = error_code;
/**
* @deprecated Error codes should be stored and returned as `error_code`, use `error_message()` instead.
*/
[[deprecated("Error codes should be stored and returned as `error_code`, use `error_message()` instead.")]]
inline const std::string error_message(int error) noexcept;
#endif // SIMDJSON_DISABLE_DEPRECATED_API
} // namespace simdjson
#endif // SIMDJSON_ERROR_H
/* end file simdjson/error.h */
/* skipped duplicate #include "simdjson/portability.h" */
/* including simdjson/concepts.h: #include "simdjson/concepts.h" */
/* begin file simdjson/concepts.h */
#ifndef SIMDJSON_CONCEPTS_H
#define SIMDJSON_CONCEPTS_H
#if SIMDJSON_SUPPORTS_CONCEPTS
#include <concepts>
#include <string_view>
#include <type_traits>
namespace simdjson {
namespace concepts {
namespace details {
#define SIMDJSON_IMPL_CONCEPT(name, method) \
template <typename T> \
concept supports_##name = !std::is_const_v<T> && requires { \
typename std::remove_cvref_t<T>::value_type; \
requires requires(typename std::remove_cvref_t<T>::value_type &&val, \
T obj) { \
obj.method(std::move(val)); \
requires !requires { obj = std::move(val); }; \
}; \
};
SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back)
SIMDJSON_IMPL_CONCEPT(emplace, emplace)
SIMDJSON_IMPL_CONCEPT(push_back, push_back)
SIMDJSON_IMPL_CONCEPT(add, add)
SIMDJSON_IMPL_CONCEPT(push, push)
SIMDJSON_IMPL_CONCEPT(append, append)
SIMDJSON_IMPL_CONCEPT(insert, insert)
SIMDJSON_IMPL_CONCEPT(op_append, operator+=)
#undef SIMDJSON_IMPL_CONCEPT
} // namespace details
template <typename T>
concept is_pair = requires { typename T::first_type; typename T::second_type; } &&
std::same_as<T, std::pair<typename T::first_type, typename T::second_type>>;
template <typename T>
concept string_view_like = std::is_convertible_v<T, std::string_view> &&
!std::is_convertible_v<T, const char*>;
template<typename T>
concept constructible_from_string_view = std::is_constructible_v<T, std::string_view>
&& !std::is_same_v<T, std::string_view>
&& std::is_default_constructible_v<T>;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* A C++20 char8_t string type such as std::u8string. Such types cannot be built
* from a std::string_view (the character types differ), so they need their own
* deserialization path, going through the u8 string accessors.
*/
template<typename T>
concept constructible_from_u8string_view = std::is_constructible_v<T, std::u8string_view>
&& !std::is_same_v<T, std::u8string_view>
&& !std::is_constructible_v<T, std::string_view>
&& std::is_default_constructible_v<T>;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename M>
concept string_view_keyed_map = string_view_like<typename M::key_type>
&& requires(std::remove_cvref_t<M>& m, typename M::key_type sv, typename M::mapped_type v) {
{ m.emplace(sv, v) } -> std::same_as<std::pair<typename M::iterator, bool>>;
};
/// Check if T is a container that we can append to, including:
/// std::vector, std::deque, std::list, std::string, ...
template <typename T>
concept appendable_containers =
(details::supports_emplace_back<T> || details::supports_emplace<T> ||
details::supports_push_back<T> || details::supports_push<T> ||
details::supports_add<T> || details::supports_append<T> ||
details::supports_insert<T>) && !string_view_keyed_map<T>;
/// Insert into the container however possible
template <appendable_containers T, typename... Args>
constexpr decltype(auto) emplace_one(T &vec, Args &&...args) {
if constexpr (details::supports_emplace_back<T>) {
return vec.emplace_back(std::forward<Args>(args)...);
} else if constexpr (details::supports_emplace<T>) {
return vec.emplace(std::forward<Args>(args)...);
} else if constexpr (details::supports_push_back<T>) {
return vec.push_back(std::forward<Args>(args)...);
} else if constexpr (details::supports_push<T>) {
return vec.push(std::forward<Args>(args)...);
} else if constexpr (details::supports_add<T>) {
return vec.add(std::forward<Args>(args)...);
} else if constexpr (details::supports_append<T>) {
return vec.append(std::forward<Args>(args)...);
} else if constexpr (details::supports_insert<T>) {
return vec.insert(std::forward<Args>(args)...);
} else if constexpr (details::supports_op_append<T> && sizeof...(Args) == 1) {
return vec.operator+=(std::forward<Args>(args)...);
} else {
static_assert(!sizeof(T *),
"We don't know how to add things to this container");
}
}
/// This checks if the container will return a reference to the newly added
/// element after an insert which for example `std::vector::emplace_back` does
/// since C++17; this will allow some optimizations.
template <typename T>
concept returns_reference = appendable_containers<T> && requires {
typename std::remove_cvref_t<T>::reference;
requires requires(typename std::remove_cvref_t<T>::value_type &&val, T obj) {
{
emplace_one(obj, std::move(val))
} -> std::same_as<typename std::remove_cvref_t<T>::reference>;
};
};
template <typename T>
concept smart_pointer = requires(std::remove_cvref_t<T> ptr) {
// Check if T has a member type named element_type
typename std::remove_cvref_t<T>::element_type;
// Check if T has a get() member function
{
ptr.get()
} -> std::same_as<typename std::remove_cvref_t<T>::element_type *>;
// Check if T can be dereferenced
{ *ptr } -> std::same_as<typename std::remove_cvref_t<T>::element_type &>;
};
template <typename T>
concept optional_type = requires(std::remove_cvref_t<T> obj) {
typename std::remove_cvref_t<T>::value_type;
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
obj.emplace(std::move(val));
{
obj.value_or(val)
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
};
{ static_cast<bool>(obj) } -> std::same_as<bool>; // convertible to bool
{ obj.reset() } noexcept -> std::same_as<void>;
};
// Types we serialize as JSON strings (not as containers)
template <typename T>
concept string_like =
std::is_same_v<std::remove_cvref_t<T>, std::string> ||
std::is_same_v<std::remove_cvref_t<T>, std::string_view> ||
std::is_same_v<std::remove_cvref_t<T>, const char*> ||
std::is_same_v<std::remove_cvref_t<T>, char*>;
// Concept that checks if a type is a container but not a string (because
// strings handling must be handled differently)
// Now uses iterator-based approach for broader container support
//
// Optional types are excluded on purpose. Since C++26 (P3168), std::optional
// is itself a range, so without the exclusion an std::optional would match
// both this concept and optional_type, making the container and the optional
// overloads of atom()/append() ambiguous. See issue 2827.
template <typename T>
concept container_but_not_string =
std::ranges::input_range<T> && !string_like<T> && !concepts::string_view_keyed_map<T>
&& !concepts::optional_type<T>;
// Concept: Indexable container that is not a string or associative container
// Accepts: std::vector, std::array, std::deque (have operator[], value_type, not string_like)
// Rejects: std::string (string_like), std::list (no operator[]), std::map (has key_type)
template<typename Container>
concept indexable_container = requires {
typename Container::value_type;
requires !concepts::string_like<Container>;
requires !requires { typename Container::key_type; }; // Reject maps/sets
requires requires(Container& c, std::size_t i) {
{ c[i] } -> std::convertible_to<typename Container::value_type>;
};
};
// Variable template to use with std::meta::substitute
template<typename Container>
constexpr bool indexable_container_v = indexable_container<Container>;
} // namespace concepts
/**
* We use tag_invoke as our customization point mechanism.
*/
template <typename Tag, typename... Args>
concept tag_invocable = requires(Tag tag, Args... args) {
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...);
};
template <typename Tag, typename... Args>
concept nothrow_tag_invocable =
tag_invocable<Tag, Args...> && requires(Tag tag, Args... args) {
{
tag_invoke(std::forward<Tag>(tag), std::forward<Args>(args)...)
} noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_CONCEPTS_H
/* end file simdjson/concepts.h */
/* including simdjson/constevalutil.h: #include "simdjson/constevalutil.h" */
/* begin file simdjson/constevalutil.h */
#ifndef SIMDJSON_CONSTEVALUTIL_H
#define SIMDJSON_CONSTEVALUTIL_H
#include <string>
#include <string_view>
#include <array>
namespace simdjson {
namespace constevalutil {
#if SIMDJSON_CONSTEVAL
constexpr static std::array<uint8_t, 256> json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
constexpr static std::array<std::string_view, 32> control_chars = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// unoptimized, meant for compile-time execution
consteval std::string consteval_to_quoted_escaped(std::string_view input) {
std::string out = "\"";
for (char c : input) {
if (json_quotable_character[uint8_t(c)]) {
if (c == '"') {
out.append("\\\"");
} else if (c == '\\') {
out.append("\\\\");
} else {
std::string_view v = control_chars[uint8_t(c)];
out.append(v);
}
} else {
out.push_back(c);
}
}
out.push_back('"');
return out;
}
consteval std::size_t padded_key_length(std::size_t length) {
return (length + 15) / 16 * 16;
}
#endif // SIMDJSON_CONSTEVAL
#if SIMDJSON_SUPPORTS_CONCEPTS
template <size_t N>
struct fixed_string {
constexpr fixed_string(const char (&str)[N]) {
for (std::size_t i = 0; i < N; ++i) {
data[i] = str[i];
}
}
constexpr fixed_string(const unsigned char (&str)[N]) {
for (std::size_t i = 0; i < N; ++i) {
data[i] = static_cast<char>(str[i]);
}
}
char data[N];
constexpr std::string_view view() const { return {data, N - 1}; }
constexpr size_t size() const { return N ; }
constexpr operator std::string_view() const { return view(); }
constexpr char operator[](std::size_t index) const { return data[index]; }
constexpr bool operator==(const fixed_string& other) const {
if (N != other.size()) {
return false;
}
for (std::size_t i = 0; i < N; ++i) {
if (data[i] != other.data[i]) {
return false;
}
}
return true;
}
};
template <std::size_t N>
fixed_string(const char (&)[N]) -> fixed_string<N>;
template <typename T>
inline constexpr bool is_fixed_string = false;
template <std::size_t N>
inline constexpr bool is_fixed_string<constevalutil::fixed_string<N>> = true;
template <fixed_string str>
struct string_constant {
static constexpr std::string_view value = str.view();
};
#if SIMDJSON_CONSTEVAL
template <typename Text>
consteval std::size_t static_string_size() {
return Text::text().size();
}
template <typename Text>
consteval std::size_t padded_static_string_size() {
return padded_key_length(static_string_size<Text>());
}
template <typename Text>
consteval auto make_static_string() {
std::array<char, padded_static_string_size<Text>()> out{};
std::string text = Text::text();
// The tail past text.size() stays zero: it is the padding.
for (std::size_t i = 0; i < text.size(); ++i) {
out[i] = text[i];
}
return out;
}
template <typename Text>
inline constexpr auto static_string = make_static_string<Text>();
template <fixed_string str>
struct quoted_escaped_text {
static consteval std::string text() { return consteval_to_quoted_escaped(str.view()); }
};
#endif // SIMDJSON_CONSTEVAL
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace constevalutil
} // namespace simdjson
#endif // SIMDJSON_CONSTEVALUTIL_H
/* end file simdjson/constevalutil.h */
#if SIMDJSON_SUPPORTS_CHAR8_T
#include <string>
#include <string_view>
#endif
/**
* @brief The top level simdjson namespace, containing everything the library provides.
*/
namespace simdjson {
SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
/** The maximum document size supported by simdjson. */
constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF;
/** The maximum depth of nested objects and arrays supported by simdjson.
A depth of SIMDJSON_MAXSIZE_BYTES/2 is not reasonable and would be
adversarial, but it serves as an upper bound for validation purposes. */
constexpr size_t SIMDJSON_MAX_DEPTH = SIMDJSON_MAXSIZE_BYTES/2;
/**
* The amount of padding needed in a buffer to parse JSON.
*
* The input buf should be readable up to buf + SIMDJSON_PADDING
* this is a stopgap; there should be a better description of the
* main loop and its behavior that abstracts over this
* See https://github.com/simdjson/simdjson/issues/174
*/
constexpr size_t SIMDJSON_PADDING = 64;
/**
* By default, simdjson supports this many nested objects and arrays.
*
* This is the default for parser::max_depth().
*/
constexpr size_t DEFAULT_MAX_DEPTH = 1024;
SIMDJSON_POP_DISABLE_UNUSED_WARNINGS
class implementation;
struct padded_string;
class padded_string_view;
enum class stage1_mode;
/**
* Stream format for parse_many/iterate_many.
*/
enum class stream_format {
whitespace_delimited, ///< Whitespace-delimited JSON documents (default, includes NDJSON/JSONL)
json_sequence, ///< RFC 7464 JSON text sequences (RS-delimited)
comma_delimited, ///< Comma-separated JSON documents (e.g., `{...},{...},{...}`)
comma_delimited_array,///< A single JSON array whose elements are iterated as
///< comma-separated documents (e.g., `[{...},{...},{...}]`).
///< The parser strips the outer `[` / `]` plus any
///< surrounding JSON whitespace (space, tab, LF, CR)
///< and then behaves like `comma_delimited` over the
///< remaining bytes.
newline_delimited ///< NDJSON/JSON Lines where each document occupies exactly
///< one line: documents are separated by line feeds and no
///< document contains a raw line feed. Same inputs as
///< `whitespace_delimited`, but the stronger guarantee lets
///< the parser find the end of a document without walking
///< it. On ondemand `iterate_many`, an unread remainder may
///< be skipped by jumping to the next line feed without
///< structure-validating that remainder. Use
///< `whitespace_delimited` if unsure.
};
namespace internal {
template<typename T>
class atomic_ptr;
class dom_parser_implementation;
class escape_json_string;
class tape_ref;
struct value128;
enum class tape_type;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Reinterpret a UTF-8 string as a C++20 std::u8string_view. No byte is copied
* or modified: char8_t and char have the same size, representation and
* alignment. Every string that simdjson produces is valid UTF-8, so this is a
* lossless view over the very same memory.
* @private
*/
simdjson_inline std::u8string_view as_u8string_view(std::string_view v) noexcept {
return std::u8string_view(reinterpret_cast<const char8_t *>(v.data()), v.size());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Assign a UTF-8 string to a string-like receiver. The general case simply
* assigns the std::string_view: it covers std::string and any user type that
* can be assigned from a std::string_view.
* @private
*/
template <typename string_type>
simdjson_inline void assign_utf8(string_type &receiver, std::string_view content) noexcept {
receiver = content;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Assign a UTF-8 string to a char8_t-based string (e.g., std::u8string). This
* overload is more specialized than the general one, so overload resolution
* prefers it whenever the receiver holds char8_t.
* @private
*/
template <typename traits_type, typename allocator_type>
simdjson_inline void assign_utf8(std::basic_string<char8_t, traits_type, allocator_type> &receiver, std::string_view content) noexcept {
receiver.assign(reinterpret_cast<const char8_t *>(content.data()), content.size());
}
/**
* Assign a UTF-8 string to a char8_t-based string view (e.g., std::u8string_view).
* @private
*/
template <typename traits_type>
simdjson_inline void assign_utf8(std::basic_string_view<char8_t, traits_type> &receiver, std::string_view content) noexcept {
receiver = std::basic_string_view<char8_t, traits_type>(reinterpret_cast<const char8_t *>(content.data()), content.size());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_BASE_H
/* end file simdjson/base.h */
/* skipped duplicate #include "simdjson/error.h" */
/* including simdjson/error-inl.h: #include "simdjson/error-inl.h" */
/* begin file simdjson/error-inl.h */
#ifndef SIMDJSON_ERROR_INL_H
#define SIMDJSON_ERROR_INL_H
/* skipped duplicate #include "simdjson/error.h" */
#include <iostream>
namespace simdjson {
inline bool is_fatal(error_code error) noexcept {
return error == TAPE_ERROR || error == INCOMPLETE_ARRAY_OR_OBJECT || error == OUT_OF_ORDER_ITERATION || error == DEPTH_ERROR;
}
namespace internal {
// We store the error code so we can validate the error message is associated with the right code
struct error_code_info {
error_code code;
const char* message; // do not use a fancy std::string where a simple C string will do (no alloc, no destructor)
};
// These MUST match the codes in error_code. We check this constraint in basictests.
extern SIMDJSON_DLLIMPORTEXPORT const error_code_info error_codes[];
} // namespace internal
inline const char *error_message(error_code error) noexcept {
// If you're using error_code, we're trusting you got it from the enum.
return internal::error_codes[int(error)].message;
}
// deprecated function
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
inline const std::string error_message(int error) noexcept {
if (error < 0 || error >= error_code::NUM_ERROR_CODES) {
return internal::error_codes[UNEXPECTED_ERROR].message;
}
return internal::error_codes[error].message;
}
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept {
return out << error_message(error);
}
namespace internal {
//
// internal::simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_inline error_code simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_inline bool simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<internal::simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline simdjson_result_base<T>::simdjson_result_base(T &&value, error_code error) noexcept
: std::pair<T, error_code>(std::forward<T>(value), error) {}
template<typename T>
simdjson_inline simdjson_result_base<T>::simdjson_result_base(error_code error) noexcept
: simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline simdjson_result_base<T>::simdjson_result_base(T &&value) noexcept
: simdjson_result_base(std::forward<T>(value), SUCCESS) {}
template<typename T>
simdjson_inline simdjson_result_base<T>::simdjson_result_base() noexcept
: simdjson_result_base(T{}, UNINITIALIZED) {}
} // namespace internal
///
/// simdjson_result<T> inline implementation
///
template<typename T>
simdjson_inline void simdjson_result<T>::tie(T &value, error_code &error) && noexcept {
std::forward<internal::simdjson_result_base<T>>(*this).tie(value, error);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code
simdjson_result<T>::get(T &value) && noexcept {
return std::forward<internal::simdjson_result_base<T>>(*this).get(value);
}
template<typename T>
simdjson_inline error_code simdjson_result<T>::error() const noexcept {
return internal::simdjson_result_base<T>::error();
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& simdjson_result<T>::value() & noexcept(false) {
return internal::simdjson_result_base<T>::value();
}
template<typename T>
simdjson_inline T&& simdjson_result<T>::value() && noexcept(false) {
return std::forward<internal::simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T&& simdjson_result<T>::take_value() && noexcept(false) {
return std::forward<internal::simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline simdjson_result<T>::operator T&&() && noexcept(false) {
return std::forward<internal::simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& simdjson_result<T>::value_unsafe() const& noexcept {
return internal::simdjson_result_base<T>::value_unsafe();
}
template<typename T>
simdjson_inline T&& simdjson_result<T>::value_unsafe() && noexcept {
return std::forward<internal::simdjson_result_base<T>>(*this).value_unsafe();
}
template<typename T>
simdjson_inline simdjson_result<T>::simdjson_result(T &&value, error_code error) noexcept
: internal::simdjson_result_base<T>(std::forward<T>(value), error) {}
template<typename T>
simdjson_inline simdjson_result<T>::simdjson_result(error_code error) noexcept
: internal::simdjson_result_base<T>(error) {}
template<typename T>
simdjson_inline simdjson_result<T>::simdjson_result(T &&value) noexcept
: internal::simdjson_result_base<T>(std::forward<T>(value)) {}
template<typename T>
simdjson_inline simdjson_result<T>::simdjson_result() noexcept
: internal::simdjson_result_base<T>() {}
} // namespace simdjson
#endif // SIMDJSON_ERROR_INL_H
/* end file simdjson/error-inl.h */
/* including simdjson/implementation.h: #include "simdjson/implementation.h" */
/* begin file simdjson/implementation.h */
#ifndef SIMDJSON_IMPLEMENTATION_H
#define SIMDJSON_IMPLEMENTATION_H
/* including simdjson/internal/atomic_ptr.h: #include "simdjson/internal/atomic_ptr.h" */
/* begin file simdjson/internal/atomic_ptr.h */
#ifndef SIMDJSON_INTERNAL_ATOMIC_PTR_H
#define SIMDJSON_INTERNAL_ATOMIC_PTR_H
/* skipped duplicate #include "simdjson/base.h" */
#include <atomic>
namespace simdjson {
namespace internal {
template<typename T>
class atomic_ptr {
public:
atomic_ptr(T *_ptr) : ptr{_ptr} {}
operator const T*() const { return ptr.load(); }
const T& operator*() const { return *ptr; }
const T* operator->() const { return ptr.load(); }
operator T*() { return ptr.load(); }
T& operator*() { return *ptr; }
T* operator->() { return ptr.load(); }
atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; }
private:
std::atomic<T*> ptr;
};
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_ATOMIC_PTR_H
/* end file simdjson/internal/atomic_ptr.h */
/* including simdjson/internal/dom_parser_implementation.h: #include "simdjson/internal/dom_parser_implementation.h" */
/* begin file simdjson/internal/dom_parser_implementation.h */
#ifndef SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H
#define SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H
/* skipped duplicate #include "simdjson/base.h" */
/* skipped duplicate #include "simdjson/error.h" */
#include <memory>
namespace simdjson {
namespace dom {
class document;
} // namespace dom
/**
* This enum is used with the dom_parser_implementation::stage1 function.
* 1) The regular mode expects a fully formed JSON document.
* 2) The streaming_partial mode expects a possibly truncated
* input within a stream on JSON documents.
* 3) The stream_final mode allows us to truncate final
* unterminated strings. It is useful in conjunction with streaming_partial.
*/
enum class stage1_mode {
regular,
streaming_partial, streaming_final,
json_sequence_partial, json_sequence_final,
comma_delimited_partial, comma_delimited_final
};
/**
* Returns true if mode == streaming_partial or mode == streaming_final
*/
inline bool is_streaming(stage1_mode mode) {
// performance note: it is probably faster to check that mode is different
// from regular than checking that it is either streaming_partial or streaming_final.
return (mode != stage1_mode::regular);
// return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final);
}
namespace internal {
/**
* An implementation of simdjson's DOM parser for a particular CPU architecture.
*
* This class is expected to be accessed only by pointer, and never move in memory (though the
* pointer can move).
*/
class dom_parser_implementation {
public:
/**
* @private For internal implementation use
*
* Run a full JSON parse on a single document (stage1 + stage2).
*
* Guaranteed only to be called when capacity > document length.
*
* Overridden by each implementation.
*
* @param buf The json document to parse. *MUST* be allocated up to len + SIMDJSON_PADDING bytes.
* @param len The length of the json document.
* @return The error code, or SUCCESS if there was no error.
*/
simdjson_warn_unused virtual error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept = 0;
/**
* @private For internal implementation use
*
* Stage 1 of the document parser.
*
* Guaranteed only to be called when capacity > document length.
*
* Overridden by each implementation.
*
* @param buf The json document to parse.
* @param len The length of the json document.
* @param streaming Whether this is being called by parser::parse_many.
* @return The error code, or SUCCESS if there was no error.
*/
simdjson_warn_unused virtual error_code stage1(const uint8_t *buf, size_t len, stage1_mode streaming) noexcept = 0;
/**
* @private For internal implementation use
*
* Stage 2 of the document parser.
*
* Called after stage1().
*
* Overridden by each implementation.
*
* @param doc The document to output to.
* @return The error code, or SUCCESS if there was no error.
*/
simdjson_warn_unused virtual error_code stage2(dom::document &doc) noexcept = 0;
/**
* @private For internal implementation use
*
* Stage 2 of the document parser for parser::parse_many.
*
* Guaranteed only to be called after stage1().
* Overridden by each implementation.
*
* @param doc The document to output to.
* @return The error code, SUCCESS if there was no error, or EMPTY if all documents have been parsed.
*/
simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0;
/**
* Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
* must be an unescaped quote terminating the string. It returns the final output
* position as pointer. In case of error (e.g., the string has bad escaped codes),
* then null_ptr is returned. It is assumed that the output buffer is large
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
* SIMDJSON_PADDING bytes.
*
* Overridden by each implementation.
*
* @param str pointer to the beginning of a valid UTF-8 JSON string, must end with an unescaped quote.
* @param dst pointer to a destination buffer, it must point a region in memory of sufficient size.
* @param allow_replacement whether we allow a replacement character when the UTF-8 contains unmatched surrogate pairs.
* @return end of the of the written region (exclusive) or nullptr in case of error.
*/
simdjson_warn_unused virtual uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept = 0;
/**
* Unescape a NON-valid UTF-8 string from src to dst, stopping at a final unescaped quote. There
* must be an unescaped quote terminating the string. It returns the final output
* position as pointer. In case of error (e.g., the string has bad escaped codes),
* then null_ptr is returned. It is assumed that the output buffer is large
* enough. E.g., if src points at 'joe"', then dst needs to have four free bytes +
* SIMDJSON_PADDING bytes.
*
* Overridden by each implementation.
*
* @param str pointer to the beginning of a possibly invalid UTF-8 JSON string, must end with an unescaped quote.
* @param dst pointer to a destination buffer, it must point a region in memory of sufficient size.
* @return end of the of the written region (exclusive) or nullptr in case of error.
*/
simdjson_warn_unused virtual uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept = 0;
/**
* Change the capacity of this parser.
*
* The capacity can never exceed SIMDJSON_MAXSIZE_BYTES (e.g., 4 GB)
* and an CAPACITY error is returned if it is attempted.
*
* Generally used for reallocation.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth.
* @return The error code, or SUCCESS if there was no error.
*/
virtual error_code set_capacity(size_t capacity) noexcept = 0;
/**
* Change the max depth of this parser.
*
* Generally used for reallocation.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth.
* @return The error code, or SUCCESS if there was no error.
*/
virtual error_code set_max_depth(size_t max_depth) noexcept = 0;
/**
* Deallocate this parser.
*/
virtual ~dom_parser_implementation() = default;
/** Number of structural indices passed from stage 1 to stage 2 */
uint32_t n_structural_indexes{0};
/** Structural indices passed from stage 1 to stage 2 */
std::unique_ptr<uint32_t[]> structural_indexes{};
/** Next structural index to parse */
uint32_t next_structural_index{0};
/**
* The largest document this parser can support without reallocating.
*
* @return Current capacity, in bytes.
*/
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/**
* The maximum level of nested object and arrays supported by this parser.
*
* @return Maximum depth, in bytes.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused inline error_code allocate(size_t capacity, size_t max_depth) noexcept;
protected:
/**
* The maximum document length this parser supports.
*
* Buffers are large enough to handle any document up to this length.
*/
size_t _capacity{0};
/**
* The maximum depth (number of nested objects and arrays) supported by this parser.
*
* Defaults to DEFAULT_MAX_DEPTH.
*/
size_t _max_depth{0};
public:
/** Whether to store big integers as strings instead of returning BIGINT_ERROR */
bool _number_as_string{false};
/**
* Whether the input buffer passed to parse() is *not* padded to len +
* SIMDJSON_PADDING bytes. When true, stage 2 string parsing avoids reading
* past buf+len (it finishes the final, near-the-end bytes from a small padded
* scratch buffer). This is set only by the no-padding DOM parse entry points
* (dom::parser::parse_unpadded); the default padded fast path leaves it false
* and is unaffected.
*/
bool _unpadded{false};
protected:
// Declaring these so that subclasses can use them to implement their constructors.
simdjson_inline dom_parser_implementation() noexcept;
simdjson_inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
simdjson_inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
simdjson_inline dom_parser_implementation(const dom_parser_implementation &) noexcept = delete;
simdjson_inline dom_parser_implementation &operator=(const dom_parser_implementation &other) noexcept = delete;
}; // class dom_parser_implementation
simdjson_inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
simdjson_inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
simdjson_inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
simdjson_pure simdjson_inline size_t dom_parser_implementation::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept {
return _max_depth;
}
simdjson_warn_unused
inline error_code dom_parser_implementation::allocate(size_t capacity, size_t max_depth) noexcept {
if (this->max_depth() != max_depth) {
error_code err = set_max_depth(max_depth);
if (err) { return err; }
}
if (_capacity != capacity) {
error_code err = set_capacity(capacity);
if (err) { return err; }
}
return SUCCESS;
}
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/internal/dom_parser_implementation.h */
#include <memory>
namespace simdjson {
/**
* Validate the UTF-8 string.
*
* @param buf the string to validate.
* @param len the length of the string in bytes.
* @return true if the string is valid UTF-8.
*/
simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) noexcept;
/**
* Validate the UTF-8 string.
*
* @param sv the string_view to validate.
* @return true if the string is valid UTF-8.
*/
simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view sv) noexcept {
return validate_utf8(sv.data(), sv.size());
}
/**
* Validate the UTF-8 string.
*
* @param p the string to validate.
* @return true if the string is valid UTF-8.
*/
simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string& s) noexcept {
return validate_utf8(s.data(), s.size());
}
/**
* An implementation of simdjson for a particular CPU architecture.
*
* Also used to maintain the currently active implementation. The active implementation is
* automatically initialized on first use to the most advanced implementation supported by the host.
*/
class implementation {
public:
/**
* The name of this implementation.
*
* const implementation *impl = simdjson::get_active_implementation();
* cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl;
*
* @return the name of the implementation, e.g. "haswell", "westmere", "arm64".
*/
virtual std::string name() const { return std::string(_name); }
/**
* The description of this implementation.
*
* const implementation *impl = simdjson::get_active_implementation();
* cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl;
*
* @return the description of the implementation, e.g. "Intel/AMD AVX2", "Intel/AMD SSE4.2", "ARM NEON".
*/
virtual std::string description() const { return std::string(_description); }
/**
* The instruction sets this implementation is compiled against
* and the current CPU match. This function may poll the current CPU/system
* and should therefore not be called too often if performance is a concern.
*
* @return true if the implementation can be safely used on the current system (determined at runtime).
*/
bool supported_by_runtime_system() const;
/**
* @private For internal implementation use
*
* The instruction sets this implementation is compiled against.
*
* @return a mask of all required `internal::instruction_set::` values.
*/
virtual uint32_t required_instruction_sets() const { return _required_instruction_sets; }
/**
* @private For internal implementation use
*
* const implementation *impl = simdjson::get_active_implementation();
* cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl;
*
* @param capacity The largest document that will be passed to the parser.
* @param max_depth The maximum JSON object/array nesting this parser is expected to handle.
* @param dst The place to put the resulting parser implementation.
* @return the error code, or SUCCESS if there was no error.
*/
virtual error_code create_dom_parser_implementation(
size_t capacity,
size_t max_depth,
std::unique_ptr<internal::dom_parser_implementation> &dst
) const noexcept = 0;
/**
* @private For internal implementation use
*
* Minify the input string assuming that it represents a JSON string, does not parse or validate.
*
* Overridden by each implementation.
*
* @param buf the json document to minify.
* @param len the length of the json document.
* @param dst the buffer to write the minified document to. *MUST* be allocated up to len + SIMDJSON_PADDING bytes.
* @param dst_len the number of bytes written. Output only.
* @return the error code, or SUCCESS if there was no error.
*/
simdjson_warn_unused virtual error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept = 0;
/**
* Validate the UTF-8 string.
*
* Overridden by each implementation.
*
* @param buf the string to validate.
* @param len the length of the string in bytes.
* @return true if and only if the string is valid UTF-8.
*/
simdjson_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0;
protected:
/** @private Construct an implementation with the given name and description. For subclasses. */
simdjson_inline implementation(
std::string_view name,
std::string_view description,
uint32_t required_instruction_sets
) :
_name(name),
_description(description),
_required_instruction_sets(required_instruction_sets)
{
}
protected:
~implementation() = default;
private:
/**
* The name of this implementation.
*/
std::string_view _name;
/**
* The description of this implementation.
*/
std::string_view _description;
/**
* Instruction sets required for this implementation.
*/
const uint32_t _required_instruction_sets;
};
/** @private */
namespace internal {
/**
* The list of available implementations compiled into simdjson.
*/
class available_implementation_list {
public:
/** Get the list of available implementations compiled into simdjson */
simdjson_inline available_implementation_list() {}
/** Number of implementations */
size_t size() const noexcept;
/** STL const begin() iterator */
const implementation * const *begin() const noexcept;
/** STL const end() iterator */
const implementation * const *end() const noexcept;
/**
* Get the implementation with the given name.
*
* Case sensitive.
*
* const implementation *impl = simdjson::get_available_implementations()["westmere"];
* if (!impl) { exit(1); }
* if (!imp->supported_by_runtime_system()) { exit(1); }
* simdjson::get_active_implementation() = impl;
*
* @param name the implementation to find, e.g. "westmere", "haswell", "arm64"
* @return the implementation, or nullptr if the parse failed.
*/
const implementation * operator[](const std::string_view &name) const noexcept {
for (const implementation * impl : *this) {
if (impl->name() == name) { return impl; }
}
return nullptr;
}
/**
* Detect the most advanced implementation supported by the current host.
*
* This is used to initialize the implementation on startup.
*
* const implementation *impl = simdjson::available_implementation::detect_best_supported();
* simdjson::get_active_implementation() = impl;
*
* @return the most advanced supported implementation for the current host, or an
* implementation that returns UNSUPPORTED_ARCHITECTURE if there is no supported
* implementation. Will never return nullptr.
*/
const implementation *detect_best_supported() const noexcept;
};
} // namespace internal
/**
* The list of available implementations compiled into simdjson.
*/
extern SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations();
/**
* The active implementation.
*
* Automatically initialized on first use to the most advanced implementation supported by this hardware.
*/
extern SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr<const implementation>& get_active_implementation();
} // namespace simdjson
#endif // SIMDJSON_IMPLEMENTATION_H
/* end file simdjson/implementation.h */
/* including simdjson/minify.h: #include "simdjson/minify.h" */
/* begin file simdjson/minify.h */
#ifndef SIMDJSON_MINIFY_H
#define SIMDJSON_MINIFY_H
/* skipped duplicate #include "simdjson/base.h" */
/* including simdjson/padded_string.h: #include "simdjson/padded_string.h" */
/* begin file simdjson/padded_string.h */
#ifndef SIMDJSON_PADDED_STRING_H
#define SIMDJSON_PADDED_STRING_H
/* skipped duplicate #include "simdjson/base.h" */
/* skipped duplicate #include "simdjson/error.h" */
/* skipped duplicate #include "simdjson/error-inl.h" */
#include <cstring>
#include <memory>
#include <string>
#include <ostream>
namespace simdjson {
class padded_string_view;
/**
* String with extra allocation for ease of use with parser::parse()
*
* This is a move-only class, it cannot be copied.
*/
struct padded_string final {
/**
* Create a new, empty padded string.
*/
explicit inline padded_string() noexcept;
/**
* Create a new padded string buffer.
*
* @param length the size of the string.
*/
explicit inline padded_string(size_t length) noexcept;
/**
* Create a new padded string by copying the given input.
*
* @param data the buffer to copy
* @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.
*
* @param str_ the string to copy
*/
inline padded_string(const std::string & str_ ) noexcept;
/**
* Create a new padded string by copying the given input.
*
* @param sv_ the string to copy
*/
inline padded_string(std::string_view sv_) noexcept;
/**
* Move one padded string into another.
*
* The original padded string will be reduced to zero capacity.
*
* @param o the string to move.
*/
inline padded_string(padded_string &&o) noexcept;
/**
* Move one padded string into another.
*
* The original padded string will be reduced to zero capacity.
*
* @param o the string to move.
*/
inline padded_string &operator=(padded_string &&o) noexcept;
inline void swap(padded_string &o) noexcept;
~padded_string() noexcept;
/**
* The length of the string.
*
* Does not include padding.
*/
size_t size() const noexcept;
/**
* The length of the string.
*
* Does not include padding.
*/
size_t length() const noexcept;
/**
* The string data.
**/
const char *data() const noexcept;
const uint8_t *u8data() const noexcept { return static_cast<const uint8_t*>(static_cast<const void*>(data_ptr));}
/**
* The string data.
**/
char *data() noexcept;
/**
* Append data to the padded string. Return true on success, false on failure.
* The complexity is O(n) where n is the new size of the string. If you are
* doing multiple appends, consider using padded_string_builder for better performance.
*
* @param data the buffer to append
* @param length the number of bytes to append
*/
inline bool append(const char *data, size_t length) noexcept;
/**
* Create a std::string_view with the same content.
*/
operator std::string_view() const;
/**
* Create a padded_string_view with the same content.
*/
operator padded_string_view() const noexcept;
/**
* Load this padded string from a file.
*
* ## Windows and Unicode
*
* Windows users who need to read files with non-ANSI characters in the
* name should set their code page to UTF-8 (65001) before calling this
* function. This should be the default with Windows 11 and better.
* Further, they may use the AreFileApisANSI function to determine whether
* the filename is interpreted using the ANSI or the system default OEM
* codepage, and they may call SetFileApisToOEM accordingly.
*
* @return IO_ERROR on error. Be mindful that on some 32-bit systems,
* the file size might be limited to 2 GB.
*
* @param path the path to the file.
**/
inline static simdjson_result<padded_string> load(std::string_view path) noexcept;
#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17
/**
* This function accepts a wide string path (UTF-16) and converts it to
* UTF-8 before loading the file. This allows windows users to work
* with unicode file paths without manually converting the paths every time.
*
* @return IO_ERROR on error, including conversion failures.
*
* @param path the path to the file as a wide string.
**/
inline static simdjson_result<padded_string> load(std::wstring_view path) noexcept;
#endif
private:
friend class padded_string_builder;
padded_string &operator=(const padded_string &o) = delete;
padded_string(const padded_string &o) = delete;
size_t viable_size{0};
char *data_ptr{nullptr};
}; // padded_string
/**
* Builder for constructing padded_string incrementally.
*
* This class allows efficient appending of data and then building a padded_string.
*/
class padded_string_builder {
public:
/**
* Create a new, empty padded string builder.
*/
inline padded_string_builder() noexcept;
/**
* Create a new padded string builder with initial capacity.
*
* @param capacity the initial capacity of the builder.
*/
inline padded_string_builder(size_t capacity) noexcept;
/**
* Move constructor.
*/
inline padded_string_builder(padded_string_builder &&o) noexcept;
/**
* Move assignment.
*/
inline padded_string_builder &operator=(padded_string_builder &&o) noexcept;
/**
* Copy constructor (deleted).
*/
padded_string_builder(const padded_string_builder &) = delete;
/**
* Copy assignment (deleted).
*/
padded_string_builder &operator=(const padded_string_builder &) = delete;
/**
* Destructor.
*/
inline ~padded_string_builder() noexcept;
/**
* Append data to the builder.
*
* @param newdata the buffer to append
* @param length the number of bytes to append
* @return true if the append succeeded, false if allocation failed
*/
inline bool append(const char *newdata, size_t length) noexcept;
/**
* Append a string view to the builder.
*
* @param sv the string view to append
* @return true if the append succeeded, false if allocation failed
*/
inline bool append(std::string_view sv) noexcept;
/**
* Get the current length of the built string.
*/
inline size_t length() const noexcept;
/**
* Build a padded_string from the current content. The builder's content
* is not modified. If you want to avoid the copy, use convert() instead.
*
* @return a padded_string containing a copy of the built content.
*/
inline padded_string build() const noexcept;
/**
* Convert the current content into a padded_string. The
* builder's content is emptied, the capacity is lost.
*
* @return a padded_string containing the built content.
*/
inline padded_string convert() noexcept;
private:
size_t size{0};
size_t capacity{0};
char *data{nullptr};
/**
* Ensure the builder has enough capacity.
*
* @param additional the additional capacity needed.
* @return true if the reservation succeeded, false if allocation failed
*/
inline bool reserve(size_t additional) noexcept;
};
/**
* Send padded_string instance to an output stream.
*
* @param out The output stream.
* @param s The padded_string instance.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, const padded_string& s) { return out << s.data(); }
#if SIMDJSON_EXCEPTIONS
/**
* Send padded_string instance to an output stream.
*
* @param out The output stream.
* @param s The padded_string instance.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string> &s) noexcept(false) { return out << s.value(); }
#endif
#if SIMDJSON_HAS_PADDED_MEMORY_MAP
/**
* A class representing a memory-mapped file with padding.
*
* On POSIX systems (Linux, macOS, BSD, ...), this uses `mmap` to map the file
* contents directly into memory, which is efficient for large files (no copy).
*
* On Windows, this class is disabled by default and must be opted into at
* build time by defining `SIMDJSON_ENABLE_MEMORY_FILE_MAPPING_ON_WINDOWS=1`. When
* enabled, `<windows.h>` must also be included before `<simdjson.h>` and
* the compilation must target Windows 10, version 1803 or later. The
* Windows implementation uses the modern memory APIs (`VirtualAlloc2`,
* `CreateFileMapping2`, `MapViewOfFile3`) with the placeholder virtual
* memory mechanism to always achieve true zero-copy mapping with
* contiguous zero-filled padding.
*
* Either way, the resulting `padded_string_view` carries at least
* `SIMDJSON_PADDING` bytes of accessible zero-filled padding after the file
* content, so it can be consumed directly by the simdjson parsers (including
* `parse_many` / `iterate_many`).
*/
class padded_memory_map {
public:
/**
* Create a new padded memory map for the given file.
* After creating the memory map, you can call view() to get a padded_string_view of the file content.
* The memory map will be automatically released when the padded_memory_map instance is destroyed.
* On POSIX systems, the file content is not copied, so this is efficient for large files.
* On Windows, the file is mapped into memory via `MapViewOfFile3` (zero-copy).
* In all cases, the file must remain unchanged while the memory map is in use.
* In case of error (e.g., file not found, permission denied, etc.), the memory map will be
* invalid and view() will return an empty view.
* You can check if the memory map is valid by calling is_valid() before using view().
*
* @param filename the path to the file to memory-map.
*/
simdjson_inline padded_memory_map(const char *filename) noexcept;
/**
* Destroy the padded memory map and release any resources.
*/
simdjson_inline ~padded_memory_map() noexcept;
// lifetime of the view is tied to the memory map, so we can return a view
// directly
/**
* Get a view of the memory-mapped file. It always succeeds, but the view may be empty
* if the memory map is invalid (e.g., due to file not found, permission denied, etc.).
* You can check if the memory map is valid by calling is_valid() before using the view.
*
* Lifetime of the view is tied to the memory map, so the view should not be used after the
* padded_memory_map instance is destroyed.
*
* @return a padded_string_view representing the memory-mapped file, or an empty view if the memory map is invalid.
*/
simdjson_inline simdjson::padded_string_view view() const noexcept simdjson_lifetime_bound;
/**
* Check if the memory map is valid.
*
* @return true if the memory map is valid, false otherwise.
*/
simdjson_inline bool is_valid() const noexcept;
private:
padded_memory_map() = delete;
padded_memory_map(const padded_memory_map &) = delete;
padded_memory_map &operator=(const padded_memory_map &) = delete;
const char *data{nullptr};
size_t size{0};
#ifdef _WIN32
// When the file ends near an allocation-granularity boundary, we use the
// placeholder API to append zero-filled padding pages. This pointer tracks
// that region so the destructor can release it with VirtualFree.
void *padding_view_{nullptr};
#endif
};
#endif // SIMDJSON_HAS_PADDED_MEMORY_MAP
} // namespace simdjson
// This is deliberately outside of simdjson so that people get it without having to use the namespace
inline simdjson::padded_string operator ""_padded(const char *str, size_t len);
#ifdef __cpp_char8_t
inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len);
#endif
namespace simdjson {
namespace internal {
// The allocate_padded_buffer function is a low-level function to allocate memory
// with padding so we can read past the "length" bytes safely. It is used by
// the padded_string class automatically. It returns nullptr in case
// of error: the caller should check for a null pointer.
// The length parameter is the maximum size in bytes of the string.
// The caller is responsible to free the memory (e.g., delete[] (...)).
inline char *allocate_padded_buffer(size_t length) noexcept;
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_PADDED_STRING_H
/* end file simdjson/padded_string.h */
#include <string>
#include <ostream>
#include <sstream>
namespace simdjson {
/**
*
* Minify the input string assuming that it represents a JSON string, does not parse or validate.
* This function is much faster than parsing a JSON string and then writing a minified version of it.
* However, it does not validate the input. It will merely return an error in simple cases (e.g., if
* there is a string that was never terminated).
*
*
* @param buf the json document to minify.
* @param len the length of the json document.
* @param dst the buffer to write the minified document to. *MUST* be allocated up to len bytes.
* @param dst_len the number of bytes written. Output only.
* @return the error code, or SUCCESS if there was no error.
*/
simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept;
} // namespace simdjson
#endif // SIMDJSON_MINIFY_H
/* end file simdjson/minify.h */
/* skipped duplicate #include "simdjson/padded_string.h" */
/* including simdjson/padded_string-inl.h: #include "simdjson/padded_string-inl.h" */
/* begin file simdjson/padded_string-inl.h */
#ifndef SIMDJSON_PADDED_STRING_INL_H
#define SIMDJSON_PADDED_STRING_INL_H
/* skipped duplicate #include "simdjson/padded_string.h" */
/* including simdjson/padded_string_view.h: #include "simdjson/padded_string_view.h" */
/* begin file simdjson/padded_string_view.h */
#ifndef SIMDJSON_PADDED_STRING_VIEW_H
#define SIMDJSON_PADDED_STRING_VIEW_H
/* skipped duplicate #include "simdjson/portability.h" */
/* skipped duplicate #include "simdjson/base.h" // for SIMDJSON_PADDING */
/* skipped duplicate #include "simdjson/error.h" */
#include <cstring>
#include <memory>
#include <string>
#include <ostream>
#if SIMDJSON_CPLUSPLUS17
#include <variant>
#endif
namespace simdjson {
/**
* User-provided string that promises it has extra padded bytes at the end for use with parser::parse().
*/
class padded_string_view : public std::string_view {
private:
size_t _capacity{0};
public:
/** Create an empty padded_string_view. */
inline padded_string_view() noexcept = default;
/**
* Promise the given buffer has at least SIMDJSON_PADDING extra bytes allocated to it.
*
* @param s The string.
* @param len The length of the string (not including padding).
* @param capacity The allocated length of the string, including padding. If the capacity is less
* than the length, the capacity will be set to the length.
*/
explicit inline padded_string_view(const char* s, size_t len, size_t capacity) noexcept;
/** overload explicit inline padded_string_view(const char* s, size_t len) noexcept */
explicit inline padded_string_view(const uint8_t* s, size_t len, size_t capacity) noexcept;
#ifdef __cpp_char8_t
explicit inline padded_string_view(const char8_t* s, size_t len, size_t capacity) noexcept;
#endif
/**
* Promise the given string has at least SIMDJSON_PADDING extra bytes allocated to it.
*
* The capacity of the string will be used to determine its padding.
*
* @param s The string.
*/
explicit inline padded_string_view(const std::string &s) noexcept;
/**
* Promise the given string_view has at least SIMDJSON_PADDING extra bytes allocated to it.
*
* @param s The string.
* @param capacity The allocated length of the string, including padding. If the capacity is less
* than the length, the capacity will be set to the length.
*/
explicit inline padded_string_view(std::string_view s, size_t capacity) noexcept;
/** The number of allocated bytes. */
inline size_t capacity() const noexcept;
/** check that the view has sufficient padding */
inline bool has_sufficient_padding() const noexcept;
/**
* Remove the UTF-8 Byte Order Mark (BOM) if it exists.
*
* @return whether a BOM was found and removed
*/
inline bool remove_utf8_bom() noexcept;
/** The amount of padding on the string (capacity() - length()) */
inline size_t padding() const noexcept;
}; // padded_string_view
/**
* Get the system's memory page size. By default, we return
* 4096 bytes, which is the most common page size. On systems
* where the page size is not a multiple of 4096 bytes, and not
* a unix-like system, nor Windows, this function may return an
* incorrect value.
*
* @return The page size in bytes.
*/
inline uint32_t get_page_size() noexcept;
#if SIMDJSON_CPLUSPLUS17
/**
* A padded_input is a wrapper around either a padded_string_view or a padded_string.
* It will automatically pad a string_view if it does not have sufficient padding
* up to the end of the memory page. Note that a requirement for this method to
* make sense is to be on a system with a page size of at least 4096 (which is
* universal except on some embedded systems).
*/
struct padded_input {
/**
* Construct a padded_input from a string_view. If the string_view does not have sufficient padding,
* the data will be copied into a padded_string and the padded_string_view will point to the
* padded_string's data. Otherwise, the padded_string_view will point to the original string_view's data.
*/
inline explicit padded_input(std::string_view sv);
/**
* Construct a padded_input from a C-style string (length specified). If the string does not have sufficient padding,
* the data will be copied into a padded_string and the padded_string_view will point to the
* padded_string's data. Otherwise, the padded_string_view will point to the original string's data.
*/
inline explicit padded_input(const char *data, size_t length);
/**
* Construct a padded_input from a std::string. If the string does not have sufficient padding
* (considering its capacity), the data will be copied into a padded_string and the padded_string_view
* will point to the padded_string's data. Otherwise, the padded_string_view will point to the
* original string's data.
*/
inline explicit padded_input(const std::string &s);
/**
* Check if the padded_input is a view.
*
* @return true if the padded_input is a view, false otherwise.
*/
inline bool is_view() const noexcept;
/**
* Convert the padded_input to a padded_string_view.
*
* @return The padded_string_view.
*/
inline operator simdjson::padded_string_view() const noexcept;
private:
std::variant<simdjson::padded_string_view, simdjson::padded_string> storage;
// whether we cross a page boundary and need to allocate a new padded string.
static inline bool needs_allocation(const char* buf, size_t len, size_t padding = SIMDJSON_PADDING) noexcept;
};
#endif // SIMDJSON_CPLUSPLUS17
#if SIMDJSON_EXCEPTIONS
/**
* Send padded_string instance to an output stream.
*
* @param out The output stream.
* @param s The padded_string_view.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string_view> &s) noexcept(false);
#endif
/**
* Create a padded_string_view from a string. The string will be padded with up to SIMDJSON_PADDING
* space characters. The resulting padded_string_view will have a length equal to the original
* string, except maybe for trailing white space characters.
*
* @param s The string.
* @return The padded string.
*/
inline padded_string_view pad(std::string& s) noexcept;
/**
* Create a padded_string_view from a string. The capacity of the string will be padded with SIMDJSON_PADDING
* characters. The resulting padded_string_view will have a length equal to the original
* string.
*
* @param s The string.
* @return The padded string.
*/
inline padded_string_view pad_with_reserve(std::string& s) noexcept;
/**
* Return the index-th document-aligned slice of a delimited stream.
*
* The input is divided into blocks of block_size bytes and each boundary is
* moved forward to just past the next delimiter, so a document is never split.
* The delimiter must not occur inside a document: a line feed for NDJSON, a
* record separator (0x1E) for RFC 7464.
*
* Slices are contiguous and non-overlapping, and each may be parsed
* independently, so callers can process them on as many threads as they like.
*
* Iterate while index * block_size < data.size(). A slice is empty when its
* block falls entirely inside one document, which happens only if that document
* is longer than block_size; skip it and continue. With block_size larger than
* the longest document, no slice is ever empty.
*
* @param data The padded input.
* @param delimiter The byte that separates documents.
* @param block_size The nominal slice size, before snapping.
* @param index Which slice to return, counting from zero.
*/
inline padded_string_view slice_at(padded_string_view data, char delimiter,
size_t block_size, size_t index) noexcept;
} // namespace simdjson
#endif // SIMDJSON_PADDED_STRING_VIEW_H
/* end file simdjson/padded_string_view.h */
/* skipped duplicate #include "simdjson/error-inl.h" */
/* including simdjson/padded_string_view-inl.h: #include "simdjson/padded_string_view-inl.h" */
/* begin file simdjson/padded_string_view-inl.h */
#ifndef SIMDJSON_PADDED_STRING_VIEW_INL_H
#define SIMDJSON_PADDED_STRING_VIEW_INL_H
/* skipped duplicate #include "simdjson/padded_string_view.h" */
/* skipped duplicate #include "simdjson/error-inl.h" */
#include <cstring> /* memcmp */
// for page size computation.
#if SIMDJSON_HAS_UNISTD_H
#include <unistd.h>
#if defined(__APPLE__)
#include <sys/sysctl.h>
#endif
#endif
namespace simdjson {
inline padded_string_view::padded_string_view(const char* s, size_t len, size_t capacity) noexcept
: std::string_view(s, len), _capacity(capacity)
{
if(_capacity < len) { _capacity = len; }
}
inline padded_string_view::padded_string_view(const uint8_t* s, size_t len, size_t capacity) noexcept
: padded_string_view(reinterpret_cast<const char*>(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<const char*>(s), len, capacity)
{
}
#endif
inline padded_string_view::padded_string_view(const std::string &s) noexcept
: std::string_view(s), _capacity(s.capacity())
{
}
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 bool padded_string_view::has_sufficient_padding() const noexcept {
if (padding() >= SIMDJSON_PADDING) {
return true;
}
size_t missing_padding = SIMDJSON_PADDING - padding();
if(length() < missing_padding) { return false; }
for (size_t i = length() - missing_padding; i < length(); i++) {
char c = data()[i];
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
return false;
}
}
return true;
}
inline size_t padded_string_view::capacity() const noexcept { return _capacity; }
inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); }
inline bool padded_string_view::remove_utf8_bom() noexcept {
if(length() < 3) { return false; }
if (std::memcmp(data(), "\xEF\xBB\xBF", 3) == 0) {
remove_prefix(3);
_capacity -= 3;
return true;
}
return false;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<padded_string_view> &s) noexcept(false) { return out << s.value(); }
#endif
inline padded_string_view pad(std::string& s) noexcept {
size_t existing_padding = 0;
for (size_t i = s.size(); i > 0; i--) {
char c = s[i - 1];
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
existing_padding++;
} else {
break;
}
}
size_t needed_padding = 0;
if (existing_padding < SIMDJSON_PADDING) {
needed_padding = SIMDJSON_PADDING - existing_padding;
s.append(needed_padding, ' ');
}
return padded_string_view(s.data(), s.size() - needed_padding, s.size());
}
inline padded_string_view pad_with_reserve(std::string& s) noexcept {
if (s.capacity() - s.size() < SIMDJSON_PADDING) {
s.reserve(s.size() + SIMDJSON_PADDING );
}
return padded_string_view(s.data(), s.size(), s.capacity());
}
inline uint32_t get_page_size() noexcept {
#if defined(_WINDOWS_) // if and only if someone loaded Windows.h, we can get the page size from there.
// Otherwise, we assume 4096.
static const uint32_t cached = []() -> uint32_t {
SYSTEM_INFO si;
GetSystemInfo(&si);
return static_cast<std::uint32_t>(si.dwPageSize);
}();
return cached;
#elif SIMDJSON_HAS_UNISTD_H
static const uint32_t cached = []() -> uint32_t {
long page_size = sysconf(_SC_PAGESIZE);
if (page_size > 0) {
return static_cast<uint32_t>(page_size);
}
return 4096; // fallback
}();
return cached;
#else
return 4096; // fallback
#endif
}
#if SIMDJSON_CPLUSPLUS17
inline padded_input::padded_input(std::string_view sv)
: storage(simdjson::padded_string_view{}) {
if (needs_allocation(sv.data(), sv.size())) {
storage = simdjson::padded_string(sv);
} else {
storage = simdjson::padded_string_view(
sv.data(), sv.size(), sv.size() + simdjson::SIMDJSON_PADDING);
}
}
inline padded_input::padded_input(const char *data, size_t length)
: storage(simdjson::padded_string_view{}) {
if (needs_allocation(data, length)) {
storage = simdjson::padded_string(data, length);
} else {
storage = simdjson::padded_string_view(
data, length, length + simdjson::SIMDJSON_PADDING);
}
}
inline padded_input::padded_input(const std::string &s)
: storage(simdjson::padded_string_view{}) {
const size_t len = s.size();
const size_t cap = s.capacity();
// Here we have the string content from data() to data() + size(),
// but the memory is accessible from data() to data() + capacity().
const size_t needed_padding = (cap - len) < simdjson::SIMDJSON_PADDING
? simdjson::SIMDJSON_PADDING - (cap - len) : 0;
if (needed_padding > 0 && needs_allocation(s.data(), cap, needed_padding)) {
storage = simdjson::padded_string(s);
} else {
storage = simdjson::padded_string_view(
s.data(), len, len + simdjson::SIMDJSON_PADDING);
}
}
inline bool padded_input::is_view() const noexcept {
return std::holds_alternative<simdjson::padded_string_view>(storage);
}
inline padded_input::operator simdjson::padded_string_view() const noexcept {
return std::visit([](const auto& p) -> simdjson::padded_string_view {
return p;
}, storage);
}
inline bool padded_input::needs_allocation(const char* buf, size_t len, size_t padding) noexcept {
if(len == 0) { return false; }
const auto page_size = get_page_size();
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size)
+ padding >= static_cast<uintptr_t>(page_size));
}
#endif // SIMDJSON_CPLUSPLUS17
inline padded_string_view slice_at(padded_string_view data, char delimiter,
size_t block_size, size_t index) noexcept {
if (block_size == 0 || index > data.size() / block_size) { return {}; }
const size_t raw_begin = index * block_size;
if (raw_begin >= data.size()) { return {}; }
auto snap = [&](size_t want) -> size_t {
if (want >= data.size()) { return data.size(); }
const void *p = std::memchr(data.data() + want, delimiter, data.size() - want);
return p ? size_t(static_cast<const char *>(p) - data.data()) + 1 : data.size();
};
const size_t begin = (raw_begin == 0) ? 0 : snap(raw_begin);
const size_t end = snap(raw_begin + block_size);
if (begin >= end) { return {}; }
return padded_string_view(data.data() + begin, end - begin,
data.capacity() - begin);
}
} // namespace simdjson
#endif // SIMDJSON_PADDED_STRING_VIEW_INL_H
/* end file simdjson/padded_string_view-inl.h */
#include <climits>
#include <cwchar>
#if SIMDJSON_HAS_UNISTD_H
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
// On Windows, `padded_memory_map` (when it is enabled) depends on types and
// functions declared in <windows.h>. We deliberately do NOT include that
// header here: users of simdjson who want `padded_memory_map` on Windows
// must include <windows.h> themselves *before* including this header. See
// padded_string.h for the detection logic.
namespace simdjson {
namespace internal {
// The allocate_padded_buffer function is a low-level function to allocate memory
// with padding so we can read past the "length" bytes safely. It is used by
// the padded_string class automatically. It returns nullptr in case
// of error: the caller should check for a null pointer.
// The length parameter is the maximum size in bytes of the string.
// The caller is responsible to free the memory (e.g., delete[] (...)).
inline char *allocate_padded_buffer(size_t length) noexcept {
const size_t totalpaddedlength = length + SIMDJSON_PADDING;
if(totalpaddedlength<length) {
// overflow
return nullptr;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
// avoid getting out of memory
if (totalpaddedlength>(1UL<<20)) {
return nullptr;
}
#endif
char *padded_buffer = new (std::nothrow) char[totalpaddedlength];
if (padded_buffer == nullptr) {
return nullptr;
}
// We write nulls in the padded region to avoid having uninitialized
// content which may trigger warning for some sanitizers
std::memset(padded_buffer + length, 0, totalpaddedlength - length);
return padded_buffer;
} // allocate_padded_buffer()
} // namespace internal
inline padded_string::padded_string() noexcept = default;
inline padded_string::padded_string(size_t length) noexcept
: viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) {
if (data_ptr == nullptr) { viable_size = 0; }
}
inline padded_string::padded_string(const char *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, data, length);
}
if (data_ptr == nullptr) {
viable_size = 0;
}
}
#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<const char *>(data), length);
}
if (data_ptr == nullptr) {
viable_size = 0;
}
}
#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())) {
if (data_ptr == nullptr) {
viable_size = 0;
} else {
std::memcpy(data_ptr, str_.data(), str_.size());
}
}
// note: do pass std::string_view arguments by value
inline padded_string::padded_string(std::string_view sv_) noexcept
: viable_size(sv_.size()), data_ptr(internal::allocate_padded_buffer(sv_.size())) {
if(simdjson_unlikely(!data_ptr)) {
//allocation failed or zero size
viable_size = 0;
return;
}
if (sv_.size()) {
std::memcpy(data_ptr, sv_.data(), sv_.size());
}
}
inline padded_string::padded_string(padded_string &&o) noexcept
: viable_size(o.viable_size), data_ptr(o.data_ptr) {
o.data_ptr = nullptr; // we take ownership
o.viable_size = 0;
}
inline padded_string &padded_string::operator=(padded_string &&o) noexcept {
delete[] data_ptr;
data_ptr = o.data_ptr;
viable_size = o.viable_size;
o.data_ptr = nullptr; // we take ownership
o.viable_size = 0;
return *this;
}
inline void padded_string::swap(padded_string &o) noexcept {
size_t tmp_viable_size = viable_size;
char *tmp_data_ptr = data_ptr;
viable_size = o.viable_size;
data_ptr = o.data_ptr;
o.data_ptr = tmp_data_ptr;
o.viable_size = tmp_viable_size;
}
inline padded_string::~padded_string() noexcept {
delete[] data_ptr;
}
inline size_t padded_string::size() const noexcept { return viable_size; }
inline size_t padded_string::length() const noexcept { return viable_size; }
inline const char *padded_string::data() const noexcept { return data_ptr; }
inline char *padded_string::data() noexcept { return data_ptr; }
inline bool padded_string::append(const char *data, size_t length) noexcept {
if (length == 0) {
return true; // Nothing to append
}
size_t new_size = viable_size + length;
if (new_size < viable_size) {
// Overflow, cannot append
return false;
}
char *new_data_ptr = internal::allocate_padded_buffer(new_size);
if (new_data_ptr == nullptr) {
// Allocation failed, cannot append
return false;
}
// Copy existing data
if (viable_size > 0) {
std::memcpy(new_data_ptr, data_ptr, viable_size);
}
// Copy new data
std::memcpy(new_data_ptr + viable_size, data, length);
// Update
delete[] data_ptr;
data_ptr = new_data_ptr;
viable_size = new_size;
return true;
}
inline padded_string::operator std::string_view() const simdjson_lifetime_bound { return std::string_view(data(), length()); }
inline padded_string::operator padded_string_view() const noexcept simdjson_lifetime_bound {
return padded_string_view(data(), length(), length() + SIMDJSON_PADDING);
}
inline simdjson_result<padded_string> padded_string::load(std::string_view filename) noexcept {
// std::string_view is not guaranteed to be null-terminated, but std::fopen requires
// a null-terminated C string. Construct a temporary std::string to ensure null-termination.
const std::string null_terminated_filename(filename);
// Open the file
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
std::FILE *fp = std::fopen(null_terminated_filename.c_str(), "rb");
SIMDJSON_POP_DISABLE_WARNINGS
if (fp == nullptr) {
return IO_ERROR;
}
// Get the file size
int ret;
#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
ret = _fseeki64(fp, 0, SEEK_END);
#else
ret = std::fseek(fp, 0, SEEK_END);
#endif // _WIN64
if(ret < 0) {
std::fclose(fp);
return IO_ERROR;
}
#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
__int64 llen = _ftelli64(fp);
if(llen == -1L) {
std::fclose(fp);
return IO_ERROR;
}
#else
long llen = std::ftell(fp);
if((llen < 0) || (llen == LONG_MAX)) {
std::fclose(fp);
return IO_ERROR;
}
#endif
// Allocate the padded_string
size_t len = static_cast<size_t>(llen);
padded_string s(len);
if (s.data() == nullptr) {
std::fclose(fp);
return MEMALLOC;
}
// Read the padded_string
std::rewind(fp);
size_t bytes_read = std::fread(s.data(), 1, len, fp);
if (std::fclose(fp) != 0 || bytes_read != len) {
return IO_ERROR;
}
return s;
}
#if defined(_WIN32) && SIMDJSON_CPLUSPLUS17
inline simdjson_result<padded_string> padded_string::load(std::wstring_view filename) noexcept {
// std::wstring_view is not guaranteed to be null-terminated, but _wfopen requires
// a null-terminated wide C string. Construct a temporary std::wstring to ensure null-termination.
const std::wstring null_terminated_filename(filename);
// Open the file using the wide characters
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
std::FILE *fp = _wfopen(null_terminated_filename.c_str(), L"rb");
SIMDJSON_POP_DISABLE_WARNINGS
if (fp == nullptr) {
return IO_ERROR;
}
// Get the file size
int ret;
#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
ret = _fseeki64(fp, 0, SEEK_END);
#else
ret = std::fseek(fp, 0, SEEK_END);
#endif // _WIN64
if(ret < 0) {
std::fclose(fp);
return IO_ERROR;
}
#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
__int64 llen = _ftelli64(fp);
if(llen == -1L) {
std::fclose(fp);
return IO_ERROR;
}
#else
long llen = std::ftell(fp);
if((llen < 0) || (llen == LONG_MAX)) {
std::fclose(fp);
return IO_ERROR;
}
#endif
// Allocate the padded_string
size_t len = static_cast<size_t>(llen);
padded_string s(len);
if (s.data() == nullptr) {
std::fclose(fp);
return MEMALLOC;
}
// Read the padded_string
std::rewind(fp);
size_t bytes_read = std::fread(s.data(), 1, len, fp);
if (std::fclose(fp) != 0 || bytes_read != len) {
return IO_ERROR;
}
return s;
}
#endif
// padded_string_builder implementations
inline padded_string_builder::padded_string_builder() noexcept = default;
inline padded_string_builder::padded_string_builder(size_t new_capacity) noexcept {
if (new_capacity > 0) {
data = internal::allocate_padded_buffer(new_capacity);
if (data != nullptr) {
this->capacity = new_capacity;
}
}
}
inline padded_string_builder::padded_string_builder(padded_string_builder &&o) noexcept
: size(o.size), capacity(o.capacity), data(o.data) {
o.size = 0;
o.capacity = 0;
o.data = nullptr;
}
inline padded_string_builder &padded_string_builder::operator=(padded_string_builder &&o) noexcept {
if (this != &o) {
delete[] data;
size = o.size;
capacity = o.capacity;
data = o.data;
o.size = 0;
o.capacity = 0;
o.data = nullptr;
}
return *this;
}
inline padded_string_builder::~padded_string_builder() noexcept {
delete[] data;
}
inline bool padded_string_builder::append(const char *newdata, size_t length) noexcept {
if (length == 0) {
return true;
}
if (!reserve(length)) {
return false;
}
std::memcpy(data + size, newdata, length);
size += length;
return true;
}
inline bool padded_string_builder::append(std::string_view sv) noexcept {
return append(sv.data(), sv.size());
}
inline size_t padded_string_builder::length() const noexcept {
return size;
}
inline padded_string padded_string_builder::build() const noexcept {
return padded_string(data, size);
}
inline padded_string padded_string_builder::convert() noexcept {
padded_string result{};
result.data_ptr = data;
result.viable_size = size;
data = nullptr;
size = 0;
capacity = 0;
return result;
}
inline bool padded_string_builder::reserve(size_t additional) noexcept {
if (simdjson_unlikely(additional + size < size)) {
return false; // overflow: cannot satisfy request
}
size_t needed = size + additional;
if (needed <= capacity) {
return true;
}
size_t new_capacity = needed;
// We are going to grow the capacity exponentially to avoid
// repeated allocations.
if (new_capacity < 4096) {
new_capacity *= 2;
// overflow guard: ensure new_capacity + new_capacity/2 does not overflow
} else if (new_capacity + new_capacity / 2 > new_capacity) {
new_capacity += new_capacity / 2; // grow by 1.5x
}
char *new_data = internal::allocate_padded_buffer(new_capacity);
if (new_data == nullptr) {
return false; // Allocation failed
}
if (size > 0) {
std::memcpy(new_data, data, size);
}
delete[] data;
data = new_data;
capacity = new_capacity;
return true;
}
#if SIMDJSON_HAS_PADDED_MEMORY_MAP
#if SIMDJSON_HAS_UNISTD_H
simdjson_inline padded_memory_map::padded_memory_map(const char *filename) noexcept {
int fd = open(filename, O_RDONLY);
if (fd == -1) {
return; // file not found or cannot be opened, data will be nullptr
}
struct stat st;
if (fstat(fd, &st) == -1) {
close(fd);
return; // failed to get file size, data will be nullptr
}
size = static_cast<size_t>(st.st_size);
size_t total_size = size + simdjson::SIMDJSON_PADDING;
void *anon_map =
mmap(NULL, total_size, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (anon_map == MAP_FAILED) {
close(fd);
return; // failed to create anonymous mapping, data will be nullptr
}
void *file_map =
mmap(anon_map, size, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0);
if (file_map == MAP_FAILED) {
munmap(anon_map, total_size);
close(fd);
return; // failed to mmap file, data will be nullptr
}
data = static_cast<const char *>(file_map);
close(fd); // no longer needed after mapping
}
simdjson_inline padded_memory_map::~padded_memory_map() noexcept {
if (data != nullptr) {
munmap(const_cast<char *>(data), size + simdjson::SIMDJSON_PADDING);
}
}
#elif defined(_WIN32)
// Windows zero-copy implementation using placeholder virtual memory.
//
// We use the modern Windows memory APIs (VirtualAlloc2, CreateFileMapping2,
// MapViewOfFile3 -- available since Windows 10 1803) to map the file into a
// contiguous virtual address range that includes at least SIMDJSON_PADDING
// zero bytes after the file content, with no data copies.
//
// Strategy:
// 1. If rounding the file size up to the allocation granularity already
// exceeds file_size + SIMDJSON_PADDING, the OS page zero-fill provides
// the padding and we use a simple MapViewOfFile3 call.
// 2. Otherwise we reserve a contiguous placeholder region via VirtualAlloc2,
// split it at the granularity-aligned file boundary, map the file into
// the first part, and commit zero pages for the second part (padding).
simdjson_inline padded_memory_map::padded_memory_map(const char *filename) noexcept {
HANDLE file_handle = ::CreateFileA(
filename, GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file_handle == INVALID_HANDLE_VALUE) {
return;
}
LARGE_INTEGER file_size_li;
if (!::GetFileSizeEx(file_handle, &file_size_li) || file_size_li.QuadPart < 0) {
::CloseHandle(file_handle);
return;
}
#if SIMDJSON_IS_32BITS
if (static_cast<unsigned long long>(file_size_li.QuadPart) >
static_cast<unsigned long long>(SIZE_MAX - simdjson::SIMDJSON_PADDING)) {
::CloseHandle(file_handle);
return;
}
#endif
size = static_cast<size_t>(file_size_li.QuadPart);
if (size == 0) {
::CloseHandle(file_handle);
return;
}
HANDLE section = ::CreateFileMapping2(
file_handle, NULL, FILE_MAP_READ, PAGE_READONLY,
0, 0, NULL, NULL, 0);
::CloseHandle(file_handle);
if (section == NULL) {
return;
}
SYSTEM_INFO si;
::GetSystemInfo(&si);
const size_t granularity = static_cast<size_t>(si.dwAllocationGranularity);
const size_t file_region = (size + granularity - 1) & ~(granularity - 1);
const size_t total_needed = size + simdjson::SIMDJSON_PADDING;
if (file_region >= total_needed) {
// The zero-fill in the last page already covers the padding.
PVOID view = ::MapViewOfFile3(
section, ::GetCurrentProcess(), NULL, 0, 0,
0, PAGE_READONLY, NULL, 0);
::CloseHandle(section);
if (view != NULL) {
data = static_cast<const char *>(view);
}
return;
}
// We need extra zero pages beyond the file region. Use the placeholder API
// to get a contiguous virtual address range spanning both the file mapping
// and the zero-filled padding.
const size_t padding_region =
((total_needed - file_region) + granularity - 1) & ~(granularity - 1);
const size_t reserve_size = file_region + padding_region;
// Reserve a contiguous placeholder.
PVOID placeholder = ::VirtualAlloc2(
::GetCurrentProcess(), NULL, reserve_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, NULL, 0);
if (placeholder == NULL) {
::CloseHandle(section);
return;
}
// Split into two placeholders at the file_region boundary.
if (!::VirtualFree(placeholder, file_region,
MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER)) {
::VirtualFree(placeholder, 0, MEM_RELEASE);
::CloseHandle(section);
return;
}
// Map the file into the first placeholder.
PVOID file_view = ::MapViewOfFile3(
section, ::GetCurrentProcess(), placeholder, 0, file_region,
MEM_REPLACE_PLACEHOLDER, PAGE_READONLY, NULL, 0);
::CloseHandle(section);
if (file_view == NULL) {
::VirtualFree(placeholder, 0, MEM_RELEASE);
::VirtualFree(static_cast<char *>(placeholder) + file_region,
0, MEM_RELEASE);
return;
}
// Commit zero pages in the second placeholder (the padding).
void *pad = static_cast<char *>(placeholder) + file_region;
PVOID padding_ptr = ::VirtualAlloc2(
::GetCurrentProcess(), pad, padding_region,
MEM_REPLACE_PLACEHOLDER | MEM_COMMIT, PAGE_READONLY, NULL, 0);
if (padding_ptr == NULL) {
::UnmapViewOfFile(file_view);
::VirtualFree(pad, 0, MEM_RELEASE);
return;
}
data = static_cast<const char *>(file_view);
padding_view_ = padding_ptr;
}
simdjson_inline padded_memory_map::~padded_memory_map() noexcept {
if (data == nullptr) { return; }
::UnmapViewOfFile(data);
if (padding_view_ != nullptr) {
::VirtualFree(padding_view_, 0, MEM_RELEASE);
}
}
#endif // POSIX or _WIN32
simdjson_inline simdjson::padded_string_view padded_memory_map::view() const noexcept simdjson_lifetime_bound {
if(!is_valid()) {
return simdjson::padded_string_view(); // return an empty view if mapping failed
}
return simdjson::padded_string_view(data, size, size + simdjson::SIMDJSON_PADDING);
}
simdjson_inline bool padded_memory_map::is_valid() const noexcept {
return data != nullptr;
}
#endif // SIMDJSON_HAS_PADDED_MEMORY_MAP
} // namespace simdjson
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<const char *>(str), len);
}
#endif
#endif // SIMDJSON_PADDED_STRING_INL_H
/* end file simdjson/padded_string-inl.h */
/* skipped duplicate #include "simdjson/padded_string_view.h" */
/* skipped duplicate #include "simdjson/padded_string_view-inl.h" */
/* including simdjson/dom.h: #include "simdjson/dom.h" */
/* begin file simdjson/dom.h */
#ifndef SIMDJSON_DOM_H
#define SIMDJSON_DOM_H
/* including simdjson/dom/base.h: #include "simdjson/dom/base.h" */
/* begin file simdjson/dom/base.h */
#ifndef SIMDJSON_DOM_BASE_H
#define SIMDJSON_DOM_BASE_H
/* skipped duplicate #include "simdjson/base.h" */
namespace simdjson {
/**
* @brief A DOM API on top of the simdjson parser.
*/
namespace dom {
/** The default batch size for parser.parse_many() and parser.load_many() */
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* It is wasteful to allocate memory for tiny documents (e.g., 4 bytes).
*/
static constexpr size_t MINIMAL_DOCUMENT_CAPACITY = 32;
class array;
class document;
class document_stream;
class element;
class key_value_pair;
class object;
class parser;
#ifdef SIMDJSON_THREADS_ENABLED
struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
} // namespace dom
namespace internal {
template<typename T>
class string_builder;
class tape_ref;
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_DOM_BASE_H
/* end file simdjson/dom/base.h */
/* including simdjson/dom/array.h: #include "simdjson/dom/array.h" */
/* begin file simdjson/dom/array.h */
#ifndef SIMDJSON_DOM_ARRAY_H
#define SIMDJSON_DOM_ARRAY_H
#include <vector>
/* skipped duplicate #include "simdjson/dom/base.h" */
/* including simdjson/internal/tape_ref.h: #include "simdjson/internal/tape_ref.h" */
/* begin file simdjson/internal/tape_ref.h */
#ifndef SIMDJSON_INTERNAL_TAPE_REF_H
#define SIMDJSON_INTERNAL_TAPE_REF_H
/* skipped duplicate #include "simdjson/base.h" */
namespace simdjson {
namespace dom {
class document;
} // namespace dom
namespace internal {
/**
* A reference to an element on the tape. Internal only.
*/
class tape_ref {
public:
simdjson_inline tape_ref() noexcept;
simdjson_inline tape_ref(const dom::document *doc, size_t json_index) noexcept;
inline size_t after_element() const noexcept;
// The reference must point to an element boundary inside the array whose
// opening tag is at array_start, or to that array's closing tag.
inline size_t before_element(size_t array_start) const noexcept;
simdjson_inline tape_type tape_ref_type() const noexcept;
simdjson_inline uint64_t tape_value() const noexcept;
simdjson_inline bool is_double() const noexcept;
simdjson_inline bool is_int64() const noexcept;
simdjson_inline bool is_uint64() const noexcept;
simdjson_inline bool is_false() const noexcept;
simdjson_inline bool is_true() const noexcept;
simdjson_inline bool is_null_on_tape() const noexcept;// different name to avoid clash with is_null.
simdjson_inline uint32_t matching_brace_index() const noexcept;
simdjson_inline uint32_t scope_count() const noexcept;
template<typename T>
simdjson_inline T next_tape_value() const noexcept;
simdjson_inline uint32_t get_string_length() const noexcept;
simdjson_inline const char * get_c_str() const noexcept;
inline std::string_view get_string_view() const noexcept;
simdjson_inline bool is_document_root() const noexcept;
simdjson_inline bool usable() const noexcept;
/** The document this element references. */
const dom::document *doc;
/** The index of this element on `doc.tape[]` */
size_t json_index;
};
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_TAPE_REF_H
/* end file simdjson/internal/tape_ref.h */
namespace simdjson {
namespace dom {
/**
* JSON array.
*/
class array {
public:
/** Create a new, invalid array */
simdjson_inline array() noexcept;
class iterator {
public:
using value_type = element;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
using iterator_category = std::forward_iterator_tag;
/**
* Get the actual value
*/
inline reference operator*() const noexcept;
/**
* Get the next value.
*
* Part of the std::iterator interface.
*/
inline iterator& operator++() noexcept;
/**
* Get the next value.
*
* Part of the std::iterator interface.
*/
inline iterator operator++(int) noexcept;
/**
* Check if these values come from the same place in the JSON.
*
* Part of the std::iterator interface.
*/
inline bool operator!=(const iterator& other) const noexcept;
inline bool operator==(const iterator& other) const noexcept;
inline bool operator<(const iterator& other) const noexcept;
inline bool operator<=(const iterator& other) const noexcept;
inline bool operator>=(const iterator& other) const noexcept;
inline bool operator>(const iterator& other) const noexcept;
iterator() noexcept = default;
iterator(const iterator&) noexcept = default;
iterator& operator=(const iterator&) noexcept = default;
private:
simdjson_inline iterator(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape{};
friend class array;
};
/**
* A forward iterator that visits the array's elements in reverse order.
* Like iterator, it returns element handles by value and does not own the
* document. No allocation or modification of the document is performed.
*/
class reverse_iterator {
public:
using value_type = element;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
using iterator_category = std::forward_iterator_tag;
inline reference operator*() const noexcept;
inline reverse_iterator& operator++() noexcept;
inline reverse_iterator operator++(int) noexcept;
inline bool operator==(const reverse_iterator& other) const noexcept;
inline bool operator!=(const reverse_iterator& other) const noexcept;
reverse_iterator() noexcept = default;
reverse_iterator(const reverse_iterator&) noexcept = default;
reverse_iterator& operator=(const reverse_iterator&) noexcept = default;
private:
simdjson_inline reverse_iterator(const internal::tape_ref &tape, size_t array_start) noexcept;
internal::tape_ref tape{};
size_t array_start{};
friend class array;
};
/**
* Return the last array element, or rend() for an empty array.
* Incrementing the returned iterator moves toward the first element.
* A complete traversal takes O(n) time and O(1) additional space, where n
* is the number of immediate elements in the array.
* Finding the last or previous element can take O(n) time in the worst case when
* numeric payloads equal numeric type markers. Increments are amortized O(1)
* over a complete traversal; nested values do not increase this bound.
*/
inline reverse_iterator rbegin() const noexcept;
/** Return the reverse traversal sentinel, before the first element. */
inline reverse_iterator rend() const noexcept;
/**
* Return the first array element.
*
* Part of the std::iterable interface.
*/
inline iterator begin() const noexcept;
/**
* One past the last array element.
*
* Part of the std::iterable interface.
*/
inline iterator end() const noexcept;
/**
* Get the size of the array (number of immediate children).
* It is a saturated value with a maximum of 0xFFFFFF: if the value
* is 0xFFFFFF then the size is 0xFFFFFF or greater.
*/
inline size_t size() const noexcept;
/**
* Get the total number of slots used by this array on the tape.
*
* Note that this is not the same thing as `size()`, which reports the
* number of actual elements within an array (not counting its children).
*
* Since an element can use 1 or 2 slots on the tape, you can only use this
* to figure out the total size of an array (including its children,
* recursively) if you know its structure ahead of time.
**/
inline size_t number_of_slots() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* dom::parser parser;
* array a = parser.parse(R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded);
* a.at_pointer("/0/foo/a/1") == 20
* a.at_pointer("0")["foo"]["a"].at(1) == 20
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept;
/**
* Recursive function which processes the JSON path of each child element
*/
inline void process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept;
/**
* Adds support for JSONPath expression with wildcards '*'
*/
inline simdjson_result<std::vector<element>> at_path_with_wildcard(std::string_view json_path) const 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<element> at_path(std::string_view json_path) const noexcept;
/**
* Get the value at the given index. This function has linear-time complexity and
* is equivalent to the following:
*
* size_t i=0;
* for (auto element : *this) {
* if (i == index) { return element; }
* i++;
* }
* return INDEX_OUT_OF_BOUNDS;
*
* Avoid calling the at() function repeatedly.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
inline simdjson_result<element> at(size_t index) const noexcept;
/**
* Gets the values of items in an array element
* This function has linear-time complexity: the values are checked one by one.
*
* @return The child elements of an array
*/
inline std::vector<element>& get_values(std::vector<element>& out) const noexcept;
/**
* Implicitly convert object to element
*/
inline operator element() const noexcept;
private:
simdjson_inline array(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape{};
friend class element;
friend struct simdjson_result<element>;
template<typename T>
friend class simdjson::internal::string_builder;
};
} // namespace dom
/** The result of a JSON conversion that may fail. */
template<>
struct simdjson_result<dom::array> : public internal::simdjson_result_base<dom::array> {
public:
simdjson_inline simdjson_result() noexcept; ///< @private
simdjson_inline simdjson_result(dom::array value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
inline void process_json_path_of_child_elements(std::vector<dom::element>::iterator& current, std::vector<dom::element>::iterator& end, const std::string_view& path_suffix, std::vector<dom::element>& accumulator) const noexcept;
inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at(size_t index) const noexcept;
inline std::vector<dom::element>& get_values(std::vector<dom::element>& out) const noexcept;
#if SIMDJSON_EXCEPTIONS
inline dom::array::iterator begin() const noexcept(false);
inline dom::array::iterator end() const noexcept(false);
inline dom::array::reverse_iterator rbegin() const noexcept(false);
inline dom::array::reverse_iterator rend() const noexcept(false);
inline size_t size() const noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
};
} // namespace simdjson
#if SIMDJSON_SUPPORTS_RANGES
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::dom::array> = true;
#if SIMDJSON_EXCEPTIONS
template<>
inline constexpr bool enable_view<simdjson::simdjson_result<simdjson::dom::array>> = true;
#endif // SIMDJSON_EXCEPTIONS
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_DOM_ARRAY_H
/* end file simdjson/dom/array.h */
/* including simdjson/dom/document_stream.h: #include "simdjson/dom/document_stream.h" */
/* begin file simdjson/dom/document_stream.h */
#ifndef SIMDJSON_DOCUMENT_STREAM_H
#define SIMDJSON_DOCUMENT_STREAM_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* including simdjson/dom/parser.h: #include "simdjson/dom/parser.h" */
/* begin file simdjson/dom/parser.h */
#ifndef SIMDJSON_DOM_PARSER_H
#define SIMDJSON_DOM_PARSER_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* including simdjson/dom/document.h: #include "simdjson/dom/document.h" */
/* begin file simdjson/dom/document.h */
#ifndef SIMDJSON_DOM_DOCUMENT_H
#define SIMDJSON_DOM_DOCUMENT_H
/* skipped duplicate #include "simdjson/dom/base.h" */
#include <memory>
namespace simdjson {
namespace dom {
/**
* A parsed JSON document.
*
* This class cannot be copied, only moved, to avoid unintended allocations.
*/
class document {
public:
/**
* Create a document container with zero capacity.
*
* The parser will allocate capacity as needed.
*/
document() noexcept = default;
~document() noexcept = default;
/**
* Take another document's buffers.
*
* @param other The document to take. Its capacity is zeroed and it is invalidated.
*/
document(document &&other) noexcept = default;
/** @private */
document(const document &) = delete; // Disallow copying
/**
* Take another document's buffers.
*
* @param other The document to take. Its capacity is zeroed.
*/
document &operator=(document &&other) noexcept = default;
/** @private */
document &operator=(const document &) = delete; // Disallow copying
/**
* Get the root element of this document as a JSON array.
*/
element root() const noexcept;
/**
* @private Dump the raw tape for debugging.
*
* @param os the stream to output to.
* @return false if the tape is likely wrong (e.g., you did not parse a valid JSON).
*/
bool dump_raw_tape(std::ostream &os) const noexcept;
/** @private Structural values. */
std::unique_ptr<uint64_t[]> tape{};
/** @private String values.
*
* Should be at least byte_capacity.
*/
std::unique_ptr<uint8_t[]> string_buf{};
/** @private Allocate memory to support
* input JSON documents of up to len bytes.
*
* When calling this function, you lose
* all the data.
*
* The memory allocation is strict: you
* can you use this function to increase
* or lower the amount of allocated memory.
* Passing zero clears the memory.
*/
error_code allocate(size_t len) noexcept;
/** @private Capacity in bytes, in terms
* of how many bytes of input JSON we can
* support.
*/
size_t capacity() const noexcept;
private:
size_t allocated_capacity{0};
friend class parser;
}; // class document
} // namespace dom
} // namespace simdjson
#endif // SIMDJSON_DOM_DOCUMENT_H
/* end file simdjson/dom/document.h */
namespace simdjson {
namespace dom {
/**
* A persistent document parser.
*
* The parser is designed to be reused, holding the internal buffers necessary to do parsing,
* as well as memory for a single document. The parsed document is overwritten on each parse.
*
* This class cannot be copied, only moved, to avoid unintended allocations.
*
* @note Moving a parser instance may invalidate "dom::element" instances. If you need to
* preserve both the "dom::element" instances and the parser, consider wrapping the parser
* instance in a std::unique_ptr instance:
*
* std::unique_ptr<dom::parser> parser(new dom::parser{});
* auto error = parser->load(f).get(root);
*
* You can then move std::unique_ptr safely.
*
* @note This is not thread safe: one parser cannot produce two documents at the same time!
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*
* @param max_capacity The maximum document length the parser can automatically handle. The parser
* will allocate more capacity on an as needed basis (when it sees documents too big to handle)
* up to this amount. The parser still starts with zero capacity no matter what this number is:
* to allocate an initial capacity, call allocate() after constructing the parser.
* Defaults to SIMDJSON_MAXSIZE_BYTES (the largest single document simdjson can process).
*/
simdjson_inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
/**
* Take another parser's buffers and state.
*
* @param other The parser to take. Its capacity is zeroed.
*/
simdjson_inline parser(parser &&other) noexcept;
parser(const parser &) = delete; ///< @private Disallow copying
/**
* Take another parser's buffers and state.
*
* @param other The parser to take. Its capacity is zeroed.
*/
simdjson_inline parser &operator=(parser &&other) noexcept;
parser &operator=(const parser &) = delete; ///< @private Disallow copying
/** Deallocate the JSON parser. */
~parser()=default;
/**
* Load a JSON document from a file and return a reference to it.
*
* dom::parser parser;
* const element doc = parser.load("jsonexamples/twitter.json");
*
* The function is eager: the file's content is loaded in memory inside the parser instance
* and immediately parsed. The file can be deleted after the `parser.load` call.
*
* ### IMPORTANT: Document Lifetime
*
* The JSON document still lives in the parser: this is the most efficient way to parse JSON
* documents because it reuses the same buffers, but you *must* use the document before you
* destroy the parser or call parse() again.
*
* Moving the parser instance is safe, but it invalidates the element instances. You may store
* the parser instance without moving it by wrapping it inside an `unique_ptr` instance like
* so: `std::unique_ptr<dom::parser> parser(new dom::parser{});`.
*
* ### Parser Capacity
*
* If the parser's current capacity is less than the file length, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* ## Windows and Unicode
*
* Windows users who need to read files with non-ANSI characters in the
* name should set their code page to UTF-8 (65001) before calling this
* function. This should be the default with Windows 11 and better.
* Further, they may use the AreFileApisANSI function to determine whether
* the filename is interpreted using the ANSI or the system default OEM
* codepage, and they may call SetFileApisToOEM accordingly.
*
* @param path The path to load.
* @return The document, or an error:
* - IO_ERROR if there was an error opening or reading the file.
* Be mindful that on some 32-bit systems,
* the file size might be limited to 2 GB.
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails.
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<element> load(std::string_view path) & noexcept;
inline simdjson_result<element> load(std::string_view path) && = delete ;
/**
* Load a JSON document from a file into a provide document instance and return a temporary reference to it.
* It is similar to the function `load` except that instead of parsing into the internal
* `document` instance associated with the parser, it allows the user to provide a document
* instance.
*
* dom::parser parser;
* dom::document doc;
* element doc_root = parser.load_into_document(doc, "jsonexamples/twitter.json");
*
* The function is eager: the file's content is loaded in memory inside the parser instance
* and immediately parsed. The file can be deleted after the `parser.load_into_document` call.
*
* ### IMPORTANT: Document Lifetime
*
* After the call to load_into_document, the parser is no longer needed.
*
* The JSON document lives in the document instance: you must keep the document
* instance alive while you navigate through it (i.e., used the returned value from
* load_into_document). You are encourage to reuse the document instance
* many times with new data to avoid reallocations:
*
* dom::document doc;
* element doc_root1 = parser.load_into_document(doc, "jsonexamples/twitter.json");
* //... doc_root1 is a pointer inside doc
* element doc_root2 = parser.load_into_document(doc, "jsonexamples/twitter.json");
* //... doc_root2 is a pointer inside doc
* // at this point doc_root1 is no longer safe
*
* Moving the document instance is safe, but it invalidates the element instances. After
* moving a document, you can recover safe access to the document root with its `root()` method.
*
* @param doc The document instance where the parsed data will be stored (on success).
* @param path The path to load.
* @return The document, or an error:
* - IO_ERROR if there was an error opening or reading the file.
* Be mindful that on some 32-bit systems,
* the file size might be limited to 2 GB.
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails.
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<element> load_into_document(document& doc, std::string_view path) & noexcept;
inline simdjson_result<element> load_into_document(document& doc, std::string_view path) && =delete;
/**
* Parse a JSON document and return a temporary reference to it.
*
* dom::parser parser;
* element doc_root = parser.parse(buf, len);
*
* The function eagerly parses the input: the input can be modified and discarded after
* the `parser.parse(buf, len)` call has completed.
*
* ### IMPORTANT: Document Lifetime
*
* The JSON document still lives in the parser: this is the most efficient way to parse JSON
* documents because it reuses the same buffers, but you *must* use the document before you
* destroy the parser or call parse() again.
*
* Moving the parser instance is safe, but it invalidates the element instances. You may store
* the parser instance without moving it by wrapping it inside an `unique_ptr` instance like
* so: `std::unique_ptr<dom::parser> parser(new dom::parser{});`.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* If realloc_if_needed is true (the default), it is assumed that the buffer does *not* have enough padding,
* and it is copied into an enlarged temporary buffer before parsing. Thus the following is safe:
*
* const char *json = R"({"key":"value"})";
* const size_t json_len = std::strlen(json);
* simdjson::dom::parser parser;
* simdjson::dom::element element = parser.parse(json, json_len);
*
* If you set realloc_if_needed to false (e.g., parser.parse(json, json_len, false)),
* you must provide a buffer with at least SIMDJSON_PADDING extra bytes at the end.
* The benefit of setting realloc_if_needed to false is that you avoid a temporary
* memory allocation and a copy.
*
* The padded bytes may be read. It is not important how you initialize
* these bytes though we recommend a sensible default like null character values or spaces.
* For example, the following low-level code is safe:
*
* const char *json = R"({"key":"value"})";
* const size_t json_len = std::strlen(json);
* std::unique_ptr<char[]> padded_json_copy{new char[json_len + SIMDJSON_PADDING]};
* std::memcpy(padded_json_copy.get(), json, json_len);
* std::memset(padded_json_copy.get() + json_len, '\0', SIMDJSON_PADDING);
* simdjson::dom::parser parser;
* simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len, false);
*
* ### std::string references
*
* Whenever you pass an std::string reference, the parser may access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's parse function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* element doc = parser.parse(simdjson::pad(json));
*
* ### Parser Capacity
*
* If the parser's current capacity is less than len, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The JSON to parse. Must have at least len + SIMDJSON_PADDING allocated bytes, unless
* realloc_if_needed is true.
* @param len The length of the JSON.
* @param realloc_if_needed Whether to reallocate and enlarge the JSON buffer to add padding.
* @return An element pointing at the root of the document, or an error:
* - MEMALLOC if realloc_if_needed is true or the parser does not have enough capacity,
* and memory allocation fails.
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<element> parse(const uint8_t *buf, size_t len, bool realloc_if_needed = true) & noexcept;
inline simdjson_result<element> parse(const uint8_t *buf, size_t len, bool realloc_if_needed = true) && =delete;
/** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_inline simdjson_result<element> parse(const char *buf, size_t len, bool realloc_if_needed = true) & noexcept;
simdjson_inline simdjson_result<element> parse(const char *buf, size_t len, bool realloc_if_needed = true) && =delete;
/** @overload parse(const std::string &) */
simdjson_inline simdjson_result<element> parse(const std::string &s) & noexcept;
simdjson_inline simdjson_result<element> parse(const std::string &s) && =delete;
/** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_inline simdjson_result<element> parse(const padded_string &s) & noexcept;
simdjson_inline simdjson_result<element> parse(const padded_string &s) && =delete;
/** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_inline simdjson_result<element> parse(const padded_string_view &v) & noexcept;
simdjson_inline simdjson_result<element> parse(const padded_string_view &v) && =delete;
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_inline simdjson_result<element> parse(const char *buf) noexcept = delete;
/**
* Parse a JSON document whose buffer is **not** padded, in place and without
* copying it.
*
* *This feature is currently experimental.*
*
* The standard parse() methods require the input buffer to have at least
* SIMDJSON_PADDING extra readable bytes after the document (or they copy it
* into a padded buffer when realloc_if_needed is true). parse_unpadded() lifts
* that requirement: it parses directly from your buffer of exactly `len` bytes,
* never reading past `buf + len`, and never allocating a full padded copy.
*
* dom::parser parser;
* std::string_view json = get_json(); // no trailing padding needed
* dom::element doc = parser.parse_unpadded(json);
*
* This is the convenient way to use simdjson when you cannot (or do not want
* to) pad your input, e.g. a std::string_view into a larger buffer or a memory
* mapped file whose tail you do not control. It is generally a little slower
* than parsing a padded buffer with parse() (the very end of the document is
* handled with extra care), but it avoids the O(n) copy that
* parse(buf, len, true) performs when realloc_if_needed is true.
*
* The input is read but not modified, and it must remain valid (and the parser
* alive) for as long as you navigate the returned document, exactly like
* parse(buf, len, false).
*
* @param buf The JSON to parse. Only `len` bytes are read; no padding required.
* @param len The length of the JSON.
* @return An element pointing at the root of the document, or an error:
* - MEMALLOC if the parser does not have enough capacity and allocation fails.
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
* - other json errors if parsing fails.
*/
inline simdjson_result<element> parse_unpadded(const uint8_t *buf, size_t len) & noexcept;
inline simdjson_result<element> parse_unpadded(const uint8_t *buf, size_t len) && =delete;
/** @overload parse_unpadded(const uint8_t *buf, size_t len) */
simdjson_inline simdjson_result<element> parse_unpadded(const char *buf, size_t len) & noexcept;
simdjson_inline simdjson_result<element> parse_unpadded(const char *buf, size_t len) && =delete;
/** @overload parse_unpadded(const uint8_t *buf, size_t len) */
simdjson_inline simdjson_result<element> parse_unpadded(std::string_view s) & noexcept;
simdjson_inline simdjson_result<element> parse_unpadded(std::string_view s) && =delete;
/**
* Parse a non-padded JSON document into a caller-provided document instance, in
* place and without copying. This is to parse_unpadded() what
* parse_into_document() is to parse(). See parse_unpadded() for the padding and
* lifetime semantics.
*
* *This feature is currently experimental.*
*
* @param doc The document instance where the parsed data will be stored (on success).
* @param buf The JSON to parse. Only `len` bytes are read; no padding required.
* @param len The length of the JSON.
*/
inline simdjson_result<element> parse_into_document_unpadded(document& doc, const uint8_t *buf, size_t len) & noexcept;
inline simdjson_result<element> parse_into_document_unpadded(document& doc, const uint8_t *buf, size_t len) && =delete;
/**
* Parse a JSON document into a provide document instance and return a temporary reference to it.
* It is similar to the function `parse` except that instead of parsing into the internal
* `document` instance associated with the parser, it allows the user to provide a document
* instance.
*
* dom::parser parser;
* dom::document doc;
* element doc_root = parser.parse_into_document(doc, buf, len);
*
* The function eagerly parses the input: the input can be modified and discarded after
* the `parser.parse(buf, len)` call has completed.
*
* ### IMPORTANT: Document Lifetime
*
* After the call to parse_into_document, the parser is no longer needed.
*
* The JSON document lives in the document instance: you must keep the document
* instance alive while you navigate through it (i.e., used the returned value from
* parse_into_document). You are encourage to reuse the document instance
* many times with new data to avoid reallocations:
*
* dom::document doc;
* element doc_root1 = parser.parse_into_document(doc, buf1, len);
* //... doc_root1 is a pointer inside doc
* element doc_root2 = parser.parse_into_document(doc, buf1, len);
* //... doc_root2 is a pointer inside doc
* // at this point doc_root1 is no longer safe
*
* Moving the document instance is safe, but it invalidates the element instances. After
* moving a document, you can recover safe access to the document root with its `root()` method.
*
* @param doc The document instance where the parsed data will be stored (on success).
* @param buf The JSON to parse. Must have at least len + SIMDJSON_PADDING allocated bytes, unless
* realloc_if_needed is true.
* @param len The length of the JSON.
* @param realloc_if_needed Whether to reallocate and enlarge the JSON buffer to add padding.
* @return An element pointing at the root of document, or an error:
* - MEMALLOC if realloc_if_needed is true or the parser does not have enough capacity,
* and memory allocation fails.
* - CAPACITY if the parser does not have enough capacity and len > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<element> parse_into_document(document& doc, const uint8_t *buf, size_t len, bool realloc_if_needed = true) & noexcept;
inline simdjson_result<element> parse_into_document(document& doc, const uint8_t *buf, size_t len, bool realloc_if_needed = true) && =delete;
/** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_inline simdjson_result<element> parse_into_document(document& doc, const char *buf, size_t len, bool realloc_if_needed = true) & noexcept;
simdjson_inline simdjson_result<element> parse_into_document(document& doc, const char *buf, size_t len, bool realloc_if_needed = true) && =delete;
/** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_inline simdjson_result<element> parse_into_document(document& doc, const std::string &s) & noexcept;
simdjson_inline simdjson_result<element> parse_into_document(document& doc, const std::string &s) && =delete;
/** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */
simdjson_inline simdjson_result<element> parse_into_document(document& doc, const padded_string &s) & noexcept;
simdjson_inline simdjson_result<element> parse_into_document(document& doc, const padded_string &s) && =delete;
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_inline simdjson_result<element> parse_into_document(document& doc, const char *buf) noexcept = delete;
/**
* Load a file containing many JSON documents.
*
* dom::parser parser;
* for (const element doc : parser.load_many(path)) {
* cout << std::string(doc["title"]) << endl;
* }
*
* The file is loaded in memory and can be safely deleted after the `parser.load_many(path)`
* function has returned. The memory is held by the `parser` instance.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
* And, possibly, no document many have been parsed when the `parser.load_many(path)` function
* returned.
*
* If there is a UTF-8 BOM, the parser skips it.
*
* ### Format
*
* The file must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* Documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with whitespace.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Error Handling
*
* All errors are returned during iteration: if there is a global error such as memory allocation,
* it will be yielded as the first result. Iteration always stops after the first error.
*
* As with all other simdjson methods, non-exception error handling is readily available through
* the same interface, requiring you to check the error before using the document:
*
* dom::parser parser;
* dom::document_stream docs;
* auto error = parser.load_many(path).get(docs);
* if (error) { cerr << error << endl; exit(1); }
* for (auto doc : docs) {
* std::string_view title;
* if ((error = doc["title"].get(title)) { cerr << error << endl; exit(1); }
* cout << title << endl;
* }
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param path File name pointing at the concatenated JSON to parse.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB (as simdjson::dom::DEFAULT_BATCH_SIZE), which has been a reasonable sweet
* spot in our tests.
* If you set the batch_size to a value smaller than simdjson::dom::MINIMAL_BATCH_SIZE
* (currently 32B), it will be replaced by simdjson::dom::MINIMAL_BATCH_SIZE.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - IO_ERROR if there was an error opening or reading the file.
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails.
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> load_many(std::string_view path, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* dom::parser parser;
* for (element doc : parser.parse_many(buf, len)) {
* cout << std::string(doc["title"]) << endl;
* }
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
* And, possibly, no document many have been parsed when the `parser.load_many(path)` function
* returned.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop. In particular, the following is unsafe and will not compile:
*
* auto docs = parser.parse_many("[\"temporary data\"]"_padded);
* // here the string "[\"temporary data\"]" may no longer exist in memory
* // the parser instance may not have even accessed the input yet
* for (element doc : docs) {
* cout << std::string(doc["title"]) << endl;
* }
*
* The following is safe:
*
* auto json = "[\"temporary data\"]"_padded;
* auto docs = parser.parse_many(json);
* for (element doc : docs) {
* cout << std::string(doc["title"]) << endl;
* }
*
* If there is a UTF-8 BOM, the parser skips it.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with whitespace.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Error Handling
*
* All errors are returned during iteration: if there is a global error such as memory allocation,
* it will be yielded as the first result. Iteration always stops after the first error.
*
* As with all other simdjson methods, non-exception error handling is readily available through
* the same interface, requiring you to check the error before using the document:
*
* dom::parser parser;
* dom::document_stream docs;
* auto error = parser.load_many(path).get(docs);
* if (error) { cerr << error << endl; exit(1); }
* for (auto doc : docs) {
* std::string_view title;
* if ((error = doc["title"].get(title)) { cerr << error << endl; exit(1); }
* cout << title << endl;
* }
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse. Must have at least len + SIMDJSON_PADDING allocated bytes.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> parse_many(const uint8_t *buf, size_t len, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> parse_many(const char *buf, size_t len, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> parse_many(const std::string &s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
inline simdjson_result<document_stream> parse_many(const std::string &&s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> parse_many(const padded_string &s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
inline simdjson_result<document_stream> parse_many(const padded_string &&s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size)
*
* Because padded_string_view guarantees SIMDJSON_PADDING trailing bytes, this
* overload is safe to use with buffers that the caller owns elsewhere (for
* example, a padded_memory_map), with no extra copy. Without this overload,
* passing a padded_string_view would silently bind to the padded_string
* overload via an implicit conversion, allocating and copying the input, and
* -- because that temporary is destroyed at the end of the full-expression --
* leaving the returned document_stream pointing at freed memory. */
inline simdjson_result<document_stream> parse_many(const padded_string_view &v, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept;
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> parse_many(const char *buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete;
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format.
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> parse_many(const padded_string_view &v, size_t batch_size, stream_format format) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while
* the returned document_stream only holds a pointer to it: iterating the stream would
* then read freed memory. These deleted overloads also catch a std::string_view
* argument, which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> parse_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload parse_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> parse_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused inline error_code allocate(size_t capacity, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @private deprecated because it returns bool instead of error_code, which is our standard for
* failures. Use allocate() instead.
*
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return true if successful, false if allocation failed.
*/
[[deprecated("Use allocate() instead.")]]
simdjson_warn_unused inline bool allocate_capacity(size_t capacity, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept;
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* The largest document this parser can support without reallocating.
*
* @return Current capacity, in bytes.
*/
simdjson_inline size_t capacity() const noexcept;
/**
* The largest document this parser can automatically support.
*
* The parser may reallocate internal buffers as needed up to this amount.
*
* @return Maximum capacity, in bytes.
*/
simdjson_inline size_t max_capacity() const noexcept;
/**
* The maximum level of nested object and arrays supported by this parser.
*
* @return Maximum depth, in bytes.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Set max_capacity. This is the largest document this parser can automatically support.
*
* The parser may reallocate internal buffers as needed up to this amount as documents are passed
* to it.
*
* Note: To avoid limiting the memory to an absurd value, such as zero or two bytes,
* iff you try to set max_capacity to a value lower than MINIMAL_DOCUMENT_CAPACITY,
* then the maximal capacity is set to MINIMAL_DOCUMENT_CAPACITY.
*
* This call will not allocate or deallocate, even if capacity is currently above max_capacity.
*
* @param max_capacity The new maximum capacity, in bytes.
*/
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations. Set to true by default.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/** @private Use the new DOM API instead */
class Iterator;
/** @private Use simdjson_error instead */
using InvalidJSON [[deprecated("Use simdjson_error instead")]] = simdjson_error;
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<internal::dom_parser_implementation> implementation{};
/** @private Use `if (parser.parse(...).error())` instead */
bool valid{false};
/** @private Use `parser.parse(...).error()` instead */
error_code error{UNINITIALIZED};
/** @private Use `parser.parse(...).value()` instead */
document doc{};
/** @private returns true if the document parsed was valid */
[[deprecated("Use the result of parser.parse() instead")]]
inline bool is_valid() const noexcept;
/**
* @private return an error code corresponding to the last parsing attempt, see
* simdjson.h will return UNINITIALIZED if no parsing was attempted
*/
[[deprecated("Use the result of parser.parse() instead")]]
inline int get_error_code() const noexcept;
/** @private return the string equivalent of "get_error_code" */
[[deprecated("Use error_message() on the result of parser.parse() instead, or cout << error")]]
inline std::string get_error_message() const noexcept;
/** @private */
[[deprecated("Use cout << on the result of parser.parse() instead")]]
inline bool print_json(std::ostream &os) const noexcept;
/** @private Private and deprecated: use `parser.parse(...).doc.dump_raw_tape()` instead */
inline bool dump_raw_tape(std::ostream &os) const noexcept;
/**
* When enabled, big integers (exceeding uint64 range) are stored as strings
* in the tape instead of returning BIGINT_ERROR. Default: false.
*/
inline void number_as_string(bool enabled) noexcept { _number_as_string = enabled; }
inline bool number_as_string() const noexcept { return _number_as_string; }
private:
/**
* The maximum document length this parser will automatically support.
*
* The parser will not be automatically allocated above this amount.
*/
size_t _max_capacity;
/** Whether to store big integers as strings instead of returning BIGINT_ERROR */
bool _number_as_string{false};
/**
* The loaded buffer (reused each time load() is called)
*/
std::unique_ptr<char[]> loaded_bytes;
/** Capacity of loaded_bytes buffer. */
size_t _loaded_bytes_capacity{0};
// all nodes are stored on the doc.tape using a 64-bit word.
//
// strings, double and ints are stored as
// a 64-bit word with a pointer to the actual value
//
//
//
// for objects or arrays, store [ or { at the beginning and } and ] at the
// end. For the openings ([ or {), we annotate them with a reference to the
// location on the doc.tape of the end, and for then closings (} and ]), we
// annotate them with a reference to the location of the opening
//
//
/**
* Ensure we have enough capacity to handle at least desired_capacity bytes,
* and auto-allocate if not. This also allocates memory if needed in the
* internal document.
*/
inline error_code ensure_capacity(size_t desired_capacity) noexcept;
/**
* Ensure we have enough capacity to handle at least desired_capacity bytes,
* and auto-allocate if not. This also allocates memory if needed in the
* provided document.
*/
inline error_code ensure_capacity(document& doc, size_t desired_capacity) noexcept;
/** Read the file into loaded_bytes */
inline simdjson_result<size_t> read_file(std::string_view path) noexcept;
friend class parser::Iterator;
friend class document_stream;
}; // class parser
} // namespace dom
} // namespace simdjson
#endif // SIMDJSON_DOM_PARSER_H
/* end file simdjson/dom/parser.h */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace dom {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, dom::parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
dom::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
};
#endif
/**
* A forward-only stream of documents.
*
* Produced by parser::parse_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* error = parser.parse_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.parse_many(json,window);
* for(auto doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below. It is
* computed from stage-1 bookkeeping, and outside these conditions it is not
* merely imprecise, it is
* arbitrary -- it can exceed size_in_bytes() or wrap around to a huge value.
* Check it only when all of the following hold:
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
/**
* An iterator through a forward-only stream of documents.
*/
class iterator {
public:
using value_type = simdjson_result<element>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* std::string_view v = i->source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline std::string_view source() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document_stream;
};
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param format is the stream format
*/
simdjson_inline document_stream(
dom::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Pass the next batch through stage 1 and return when finished.
* When threads are enabled, this may wait for the stage 1 thread to finish.
*/
inline void load_batch() noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(dom::parser &p, size_t batch_start) noexcept;
dom::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
/** The stream format. */
stream_format format;
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
friend struct stage1_worker;
std::unique_ptr<stage1_worker> worker{new(std::nothrow) stage1_worker()};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
dom::parser stage1_thread_parser{};
#endif // SIMDJSON_THREADS_ENABLED
friend class dom::parser;
friend struct simdjson_result<dom::document_stream>;
friend struct internal::simdjson_result_base<dom::document_stream>;
}; // class document_stream
} // namespace dom
template<>
struct simdjson_result<dom::document_stream> : public internal::simdjson_result_base<dom::document_stream> {
public:
simdjson_inline simdjson_result() noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result(dom::document_stream &&value) noexcept; ///< @private
#if SIMDJSON_EXCEPTIONS
simdjson_inline dom::document_stream::iterator begin() noexcept(false);
simdjson_inline dom::document_stream::iterator end() noexcept(false);
#else // SIMDJSON_EXCEPTIONS
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
[[deprecated("parse_many() and load_many() may return errors. Use document_stream stream; error = parser.parse_many().get(doc); instead.")]]
simdjson_inline dom::document_stream::iterator begin() noexcept;
[[deprecated("parse_many() and load_many() may return errors. Use document_stream stream; error = parser.parse_many().get(doc); instead.")]]
simdjson_inline dom::document_stream::iterator end() noexcept;
#endif // SIMDJSON_DISABLE_DEPRECATED_API
#endif // SIMDJSON_EXCEPTIONS
}; // struct simdjson_result<dom::document_stream>
} // namespace simdjson
#endif // SIMDJSON_DOCUMENT_STREAM_H
/* end file simdjson/dom/document_stream.h */
/* skipped duplicate #include "simdjson/dom/document.h" */
/* including simdjson/dom/element.h: #include "simdjson/dom/element.h" */
/* begin file simdjson/dom/element.h */
#ifndef SIMDJSON_DOM_ELEMENT_H
#define SIMDJSON_DOM_ELEMENT_H
#include <vector>
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/array.h" */
namespace simdjson {
namespace dom {
/**
* The actual concrete type of a JSON element
* This is the type it is most easily cast to with get<>.
*/
enum class element_type {
ARRAY = '[', ///< dom::array
OBJECT = '{', ///< dom::object
INT64 = 'l', ///< int64_t
UINT64 = 'u', ///< uint64_t: any integer that fits in uint64_t but *not* int64_t
DOUBLE = 'd', ///< double: Any number with a "." or "e" that fits in double.
STRING = '"', ///< std::string_view
BOOL = 't', ///< bool
NULL_VALUE = 'n', ///< null
/// The BIGINT type is for integers that do not fit in 64 bits. It is only present
// if you set parser.number_as_string(true).
BIGINT = 'Z' ///< std::string_view: big integer stored as raw digit string
};
/**
* A JSON element.
*
* References an element in a JSON document, representing a JSON null, boolean, string, number,
* array or object.
*/
class element {
public:
/** Create a new, invalid element. */
simdjson_inline element() noexcept;
/** The type of this element. */
simdjson_inline element_type type() const noexcept;
/**
* Cast this element to an array.
*
* @returns An object that can be used to iterate the array, or:
* INCORRECT_TYPE if the JSON element is not an array.
*/
inline simdjson_result<array> get_array() const noexcept;
/**
* Cast this element to an object.
*
* @returns An object that can be used to look up or iterate the object's fields, or:
* INCORRECT_TYPE if the JSON element is not an object.
*/
inline simdjson_result<object> get_object() const noexcept;
/**
* Cast this element to a null-terminated C string.
*
* The string is guaranteed to be valid UTF-8.
*
* The length of the string is given by get_string_length(). Because JSON strings
* may contain null characters, it may be incorrect to use strlen to determine the
* string length.
*
* It is possible to get a single string_view instance which represents both the string
* content and its length: see get_string().
*
* @returns A pointer to a null-terminated UTF-8 string. This string is stored in the parser and will
* be invalidated the next time it parses a document or when it is destroyed.
* Returns INCORRECT_TYPE if the JSON element is not a string.
*/
inline simdjson_result<const char *> get_c_str() const noexcept;
/**
* Gives the length in bytes of the string.
*
* It is possible to get a single string_view instance which represents both the string
* content and its length: see get_string().
*
* @returns A string length in bytes.
* Returns INCORRECT_TYPE if the JSON element is not a string.
*/
inline simdjson_result<size_t> get_string_length() const noexcept;
/**
* Cast this element to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next time it
* parses a document or when it is destroyed.
* Returns INCORRECT_TYPE if the JSON element is not a string.
*/
inline simdjson_result<std::string_view> get_string() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this element to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns A std::u8string_view. The string is stored in the parser and will be invalidated the next time it
* parses a document or when it is destroyed.
* Returns INCORRECT_TYPE if the JSON element is not a string.
*/
inline simdjson_result<std::u8string_view> get_u8string() const noexcept;
#endif
/**
* Cast this element to a signed integer.
*
* @returns A signed 64-bit integer.
* Returns INCORRECT_TYPE if the JSON element is not an integer, or NUMBER_OUT_OF_RANGE
* if it is negative.
*/
inline simdjson_result<int64_t> get_int64() const noexcept;
/**
* Cast this element to an unsigned integer.
*
* @returns An unsigned 64-bit integer.
* Returns INCORRECT_TYPE if the JSON element is not an integer, or NUMBER_OUT_OF_RANGE
* if it is too large.
*/
inline simdjson_result<uint64_t> get_uint64() const noexcept;
/**
* Cast this element to a double floating-point.
*
* @returns A double value.
* Returns INCORRECT_TYPE if the JSON element is not a number.
*/
inline simdjson_result<double> get_double() const noexcept;
/**
* Cast this element to a bool.
*
* @returns A bool value.
* Returns INCORRECT_TYPE if the JSON element is not a boolean.
*/
inline simdjson_result<bool> get_bool() const noexcept;
/**
* Read this element as a big integer (raw digit string).
*
* @returns A string_view of the raw digits, or:
* INCORRECT_TYPE if the JSON element is not a big integer.
*/
inline simdjson_result<std::string_view> get_bigint() const noexcept;
/**
* Whether this element is a json array.
*
* Equivalent to is<array>().
*/
inline bool is_array() const noexcept;
/**
* Whether this element is a json object.
*
* Equivalent to is<object>().
*/
inline bool is_object() const noexcept;
/**
* Whether this element is a json string.
*
* Equivalent to is<std::string_view>() or is<const char *>().
*/
inline bool is_string() const noexcept;
/**
* Whether this element is a json number that fits in a signed 64-bit integer.
*
* Equivalent to is<int64_t>().
*/
inline bool is_int64() const noexcept;
/**
* Whether this element is a json number that fits in an unsigned 64-bit integer.
*
* Equivalent to is<uint64_t>().
*/
inline bool is_uint64() const noexcept;
/**
* Whether this element is a json number that fits in a double.
*
* Equivalent to is<double>().
*/
inline bool is_double() const noexcept;
/**
* Whether this element is a json number.
*
* Both integers and floating points will return true.
*/
inline bool is_number() const noexcept;
/**
* Whether this element is a json `true` or `false`.
*
* Equivalent to is<bool>().
*/
inline bool is_bool() const noexcept;
/**
* Whether this element is a json `null`.
*/
inline bool is_null() const noexcept;
/**
* Whether this element is a big integer (number exceeding 64-bit range).
*/
inline bool is_bigint() const noexcept;
/**
* Tell whether the value can be cast to provided type (T).
*
* Supported types:
* - Boolean: bool
* - Number: double, uint64_t, int64_t
* - String: std::string_view, const char *, std::u8string_view (C++20)
* - Array: dom::array
* - Object: dom::object
*
* @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object
*/
template<typename T>
simdjson_inline bool is() const noexcept;
/**
* Get the value as the provided type (T).
*
* Supported types:
* - Boolean: bool
* - Number: double, uint64_t, int64_t
* - String: std::string_view, const char *, std::u8string_view (C++20)
* - Array: dom::array
* - Object: dom::object
*
* You may use get_double(), get_bool(), get_uint64(), get_int64(),
* get_object(), get_array() or get_string() instead.
*
* @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object
*
* @returns The value cast to the given type, or:
* INCORRECT_TYPE if the value cannot be cast to the given type.
*/
template<typename T>
inline simdjson_result<T> get() const noexcept {
// Unless the simdjson library provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are Boolean (bool), numbers (double, uint64_t, int64_t), "
"strings (std::string_view, const char *), arrays (dom::array) and objects (dom::object). "
"We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), "
"get_object(), get_array() or get_string() instead of the get template.");
}
/**
* Get the value as the provided type (T).
*
* Supported types:
* - Boolean: bool
* - Number: double, uint64_t, int64_t
* - String: std::string_view, const char *, std::u8string_view (C++20)
* - Array: dom::array
* - Object: dom::object
*
* @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object
*
* @param value The variable to set to the value. May not be set if there is an error.
*
* @returns The error that occurred, or SUCCESS if there was no error.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &value) const noexcept;
/**
* Get the value as the provided type (T), setting error if it's not the given type.
*
* Supported types:
* - Boolean: bool
* - Number: double, uint64_t, int64_t
* - String: std::string_view, const char *, std::u8string_view (C++20)
* - Array: dom::array
* - Object: dom::object
*
* @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object
*
* @param value The variable to set to the given type. value is undefined if there is an error.
* @param error The variable to store the error. error is set to error_code::SUCCEED if there is an error.
*/
template<typename T>
inline void tie(T &value, error_code &error) && noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Read this element as a boolean.
*
* @return The boolean value
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a boolean.
*/
inline operator bool() const noexcept(false);
/**
* Read this element as a null-terminated UTF-8 string.
*
* Be mindful that JSON allows strings to contain null characters.
*
* Does *not* convert other types to a string; requires that the JSON type of the element was
* an actual string.
*
* @return The string value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a string.
*/
inline explicit operator const char*() const noexcept(false);
/**
* Read this element as a null-terminated UTF-8 string.
*
* Does *not* convert other types to a string; requires that the JSON type of the element was
* an actual string.
*
* @return The string value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a string.
*/
inline operator std::string_view() const noexcept(false);
/**
* Read this element as an unsigned integer.
*
* @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 does not fit in 64 bits or is negative
*/
inline operator uint64_t() const noexcept(false);
/**
* Read this element as an signed integer.
*
* @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 does not fit in 64 bits
*/
inline operator int64_t() const noexcept(false);
/**
* Read this element as an double.
*
* @return The double value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a number
*/
inline operator double() const noexcept(false);
/**
* Read this element as a JSON array.
*
* @return The JSON array.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array
*/
inline operator array() const noexcept(false);
/**
* Read this element as a JSON object (key/value pairs).
*
* @return The JSON object.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an object
*/
inline operator object() const noexcept(false);
/**
* Iterate over each element in this array.
*
* @return The beginning of the iteration.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array
*/
inline dom::array::iterator begin() const noexcept(false);
/**
* Iterate over each element in this array.
*
* @return The end of the iteration.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array
*/
inline dom::array::iterator end() const noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the value associated with the given key.
*
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
* - INCORRECT_TYPE if this is not an object
*/
inline simdjson_result<element> operator[](std::string_view key) const noexcept;
/**
* Get the value associated with the given key.
*
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
* - INCORRECT_TYPE if this is not an object
*/
inline simdjson_result<element> operator[](const char *key) const noexcept;
simdjson_result<element> operator[](int) const noexcept = delete;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* dom::parser parser;
* element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded);
* doc.at_pointer("/foo/a/1") == 20
* doc.at_pointer("/foo")["a"].at(1) == 20
* doc.at_pointer("")["foo"]["a"].at(1) == 20
*
* It is allowed for a key to be the empty string:
*
* dom::parser parser;
* object obj = parser.parse(R"({ "": { "a": [ 10, 20, 30 ] }})"_padded);
* obj.at_pointer("//a/1") == 20
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<element> at_pointer(const std::string_view json_pointer) const noexcept;
inline simdjson_result<std::vector<element>> at_path_with_wildcard(const std::string_view json_path) const 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<element> at_path(std::string_view json_path) const noexcept;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
*
* Version 0.4 of simdjson used an incorrect interpretation of the JSON Pointer standard
* and allowed the following :
*
* dom::parser parser;
* element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded);
* doc.at("foo/a/1") == 20
*
* Though it is intuitive, it is not compliant with RFC 6901
* https://tools.ietf.org/html/rfc6901
*
* For standard compliance, use the at_pointer function instead.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
inline simdjson_result<element> at(const std::string_view json_pointer) const noexcept;
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Get the value at the given index.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
inline simdjson_result<element> at(size_t index) const noexcept;
/**
* Get the value associated with the given key.
*
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
*/
inline simdjson_result<element> at_key(std::string_view key) const noexcept;
/**
* Get the value associated with the given key in a case-insensitive manner.
*
* Note: The key will be matched against **unescaped** JSON.
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
*/
inline simdjson_result<element> at_key_case_insensitive(std::string_view key) const noexcept;
/**
* operator< defines a total order for element allowing to use them in
* ordered C++ STL containers
*
* @return TRUE if the key appears before the other one in the tape
*/
inline bool operator<(const element &other) const noexcept;
/**
* operator== allows to verify if two element values reference the
* same JSON item
*
* @return TRUE if the two values references the same JSON element
*/
inline bool operator==(const element &other) const noexcept;
/** @private for debugging. Prints out the root element. */
inline bool dump_raw_tape(std::ostream &out) const noexcept;
private:
simdjson_inline element(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape{};
friend class document;
friend class object;
friend class array;
friend struct simdjson_result<element>;
template<typename T>
friend class simdjson::internal::string_builder;
};
} // namespace dom
/** The result of a JSON navigation that may fail. */
template<>
struct simdjson_result<dom::element> : public internal::simdjson_result_base<dom::element> {
public:
simdjson_inline simdjson_result() noexcept; ///< @private
simdjson_inline simdjson_result(dom::element &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result<dom::element_type> type() const noexcept;
template<typename T>
simdjson_inline bool is() const noexcept;
template<typename T>
simdjson_inline simdjson_result<T> get() const noexcept;
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &value) const noexcept;
simdjson_inline simdjson_result<dom::array> get_array() const noexcept;
simdjson_inline simdjson_result<dom::object> get_object() const noexcept;
simdjson_inline simdjson_result<const char *> get_c_str() const noexcept;
simdjson_inline simdjson_result<size_t> get_string_length() const noexcept;
simdjson_inline simdjson_result<std::string_view> get_string() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string() const noexcept;
#endif
simdjson_inline simdjson_result<int64_t> get_int64() const noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() const noexcept;
simdjson_inline simdjson_result<double> get_double() const noexcept;
simdjson_inline simdjson_result<bool> get_bool() const noexcept;
simdjson_inline simdjson_result<std::string_view> get_bigint() const noexcept;
simdjson_inline bool is_array() const noexcept;
simdjson_inline bool is_object() const noexcept;
simdjson_inline bool is_string() const noexcept;
simdjson_inline bool is_int64() const noexcept;
simdjson_inline bool is_uint64() const noexcept;
simdjson_inline bool is_double() const noexcept;
simdjson_inline bool is_number() const noexcept;
simdjson_inline bool is_bool() const noexcept;
simdjson_inline bool is_null() const noexcept;
simdjson_inline bool is_bigint() const noexcept;
simdjson_inline simdjson_result<dom::element> operator[](std::string_view key) const noexcept;
simdjson_inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
simdjson_result<dom::element> operator[](int) const noexcept = delete;
simdjson_inline simdjson_result<dom::element> at_pointer(const std::string_view json_pointer) const noexcept;
simdjson_inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(const std::string_view json_path) const noexcept;
simdjson_inline simdjson_result<dom::element> at_path(const std::string_view json_path) const noexcept;
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
simdjson_inline simdjson_result<dom::element> at(const std::string_view json_pointer) const noexcept;
simdjson_inline simdjson_result<dom::element> at(size_t index) const noexcept;
simdjson_inline simdjson_result<dom::element> at_key(std::string_view key) const noexcept;
simdjson_inline simdjson_result<dom::element> at_key_case_insensitive(std::string_view key) const noexcept;
#if SIMDJSON_EXCEPTIONS
simdjson_inline operator bool() const noexcept(false);
simdjson_inline explicit operator const char*() const noexcept(false);
simdjson_inline operator std::string_view() const noexcept(false);
simdjson_inline operator uint64_t() const noexcept(false);
simdjson_inline operator int64_t() const noexcept(false);
simdjson_inline operator double() const noexcept(false);
simdjson_inline operator dom::array() const noexcept(false);
simdjson_inline operator dom::object() const noexcept(false);
simdjson_inline dom::array::iterator begin() const noexcept(false);
simdjson_inline dom::array::iterator end() const noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
};
} // namespace simdjson
#endif // SIMDJSON_DOM_DOCUMENT_H
/* end file simdjson/dom/element.h */
/* including simdjson/dom/object.h: #include "simdjson/dom/object.h" */
/* begin file simdjson/dom/object.h */
#ifndef SIMDJSON_DOM_OBJECT_H
#define SIMDJSON_DOM_OBJECT_H
#include <vector>
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/element.h" */
/* skipped duplicate #include "simdjson/internal/tape_ref.h" */
namespace simdjson {
namespace dom {
/**
* JSON object.
*/
class object {
public:
/** Create a new, invalid object */
simdjson_inline object() noexcept;
class iterator {
public:
using value_type = const key_value_pair;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
using iterator_category = std::forward_iterator_tag;
/**
* Get the actual key/value pair
*/
inline reference operator*() const noexcept;
/**
* Get the next key/value pair.
*
* Part of the std::iterator interface.
*
*/
inline iterator& operator++() noexcept;
/**
* Get the next key/value pair.
*
* Part of the std::iterator interface.
*
*/
inline iterator operator++(int) noexcept;
/**
* Check if these values come from the same place in the JSON.
*
* Part of the std::iterator interface.
*/
inline bool operator!=(const iterator& other) const noexcept;
inline bool operator==(const iterator& other) const noexcept;
inline bool operator<(const iterator& other) const noexcept;
inline bool operator<=(const iterator& other) const noexcept;
inline bool operator>=(const iterator& other) const noexcept;
inline bool operator>(const iterator& other) const noexcept;
/**
* Get the key of this key/value pair.
*/
inline std::string_view key() const noexcept;
/**
* Get the length (in bytes) of the key in this key/value pair.
* You should expect this function to be faster than key().size().
*/
inline uint32_t key_length() const noexcept;
/**
* Returns true if the key in this key/value pair is equal
* to the provided string_view.
*/
inline bool key_equals(std::string_view o) const noexcept;
/**
* Returns true if the key in this key/value pair is equal
* to the provided string_view in a case-insensitive manner.
* Case comparisons may only be handled correctly for ASCII strings.
*/
inline bool key_equals_case_insensitive(std::string_view o) const noexcept;
/**
* Get the key of this key/value pair.
*/
inline const char *key_c_str() const noexcept;
/**
* Get the value of this key/value pair.
*/
inline element value() const noexcept;
iterator() noexcept = default;
iterator(const iterator&) noexcept = default;
iterator& operator=(const iterator&) noexcept = default;
private:
simdjson_inline iterator(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape{};
friend class object;
};
/**
* Return the first key/value pair.
*
* Part of the std::iterable interface.
*/
inline iterator begin() const noexcept;
/**
* One past the last key/value pair.
*
* Part of the std::iterable interface.
*/
inline iterator end() const noexcept;
/**
* Get the size of the object (number of keys).
* It is a saturated value with a maximum of 0xFFFFFF: if the value
* is 0xFFFFFF then the size is 0xFFFFFF or greater.
*/
inline size_t size() const noexcept;
/**
* Get the value associated with the given key.
*
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* This function has linear-time complexity: the keys are checked one by one.
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
* - INCORRECT_TYPE if this is not an object
*/
inline simdjson_result<element> operator[](std::string_view key) const noexcept;
/**
* Get the value associated with the given key.
*
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* This function has linear-time complexity: the keys are checked one by one.
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
* - INCORRECT_TYPE if this is not an object
*/
inline simdjson_result<element> operator[](const char *key) const noexcept;
simdjson_result<element> operator[](int) const noexcept = delete;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* dom::parser parser;
* object obj = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded);
* obj.at_pointer("/foo/a/1") == 20
* obj.at_pointer("/foo")["a"].at(1) == 20
*
* It is allowed for a key to be the empty string:
*
* dom::parser parser;
* object obj = parser.parse(R"({ "": { "a": [ 10, 20, 30 ] }})"_padded);
* obj.at_pointer("//a/1") == 20
* obj.at_pointer("/")["a"].at(1) == 20
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<element> at_pointer(std::string_view json_pointer) const noexcept;
/**
* Recursive function which processes the JSON path of each child element
*/
inline void process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept;
/**
* Adds support for JSONPath expression with wildcards '*'
*/
inline simdjson_result<std::vector<element>> at_path_with_wildcard(std::string_view json_path) const 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<element> at_path(std::string_view json_path) const noexcept;
/**
* Get the value associated with the given key.
*
* The key will be matched against **unescaped** JSON:
*
* dom::parser parser;
* int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1
* parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD
*
* This function has linear-time complexity: the keys are checked one by one.
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
*/
inline simdjson_result<element> at_key(std::string_view key) const noexcept;
/**
* Gets the values associated with keys of an object
* This function has linear-time complexity: the keys are checked one by one.
*
* @return the values associated with each key of an object
*/
inline std::vector<element>& get_values(std::vector<element>& out) const noexcept;
/**
* Get the value associated with the given key in a case-insensitive manner.
* It is only guaranteed to work over ASCII inputs.
*
* Note: The key will be matched against **unescaped** JSON.
*
* This function has linear-time complexity: the keys are checked one by one.
*
* @return The value associated with this field, or:
* - NO_SUCH_FIELD if the field does not exist in the object
*/
inline simdjson_result<element> at_key_case_insensitive(std::string_view key) const noexcept;
/**
* Implicitly convert object to element
*/
inline operator element() const noexcept;
private:
simdjson_inline object(const internal::tape_ref &tape) noexcept;
internal::tape_ref tape{};
friend class element;
friend struct simdjson_result<element>;
template<typename T>
friend class simdjson::internal::string_builder;
};
/**
* Key/value pair in an object.
*/
class key_value_pair {
public:
/** key in the key-value pair **/
std::string_view key;
/** value in the key-value pair **/
element value;
private:
simdjson_inline key_value_pair(std::string_view _key, element _value) noexcept;
friend class object;
};
} // namespace dom
/** The result of a JSON conversion that may fail. */
template<>
struct simdjson_result<dom::object> : public internal::simdjson_result_base<dom::object> {
public:
simdjson_inline simdjson_result() noexcept; ///< @private
simdjson_inline simdjson_result(dom::object value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
inline simdjson_result<dom::element> operator[](std::string_view key) const noexcept;
inline simdjson_result<dom::element> operator[](const char *key) const noexcept;
simdjson_result<dom::element> operator[](int) const noexcept = delete;
inline simdjson_result<dom::element> at_pointer(std::string_view json_pointer) const noexcept;
inline void process_json_path_of_child_elements(std::vector<dom::element>::iterator& current, std::vector<dom::element>::iterator& end, const std::string_view& path_suffix, std::vector<dom::element>& accumulator) const noexcept;
inline simdjson_result<std::vector<dom::element>> at_path_with_wildcard(std::string_view json_path_new) const noexcept;
inline simdjson_result<dom::element> at_path(std::string_view json_path) const noexcept;
inline simdjson_result<dom::element> at_key(std::string_view key) const noexcept;
inline std::vector<dom::element>& get_values(std::vector<dom::element>& out) const noexcept;
inline simdjson_result<dom::element> at_key_case_insensitive(std::string_view key) const noexcept;
#if SIMDJSON_EXCEPTIONS
inline dom::object::iterator begin() const noexcept(false);
inline dom::object::iterator end() const noexcept(false);
inline size_t size() const noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
};
} // namespace simdjson
#if SIMDJSON_SUPPORTS_RANGES
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::dom::object> = true;
#if SIMDJSON_EXCEPTIONS
template<>
inline constexpr bool enable_view<simdjson::simdjson_result<simdjson::dom::object>> = true;
#endif // SIMDJSON_EXCEPTIONS
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_DOM_OBJECT_H
/* end file simdjson/dom/object.h */
/* skipped duplicate #include "simdjson/dom/parser.h" */
/* including simdjson/dom/serialization.h: #include "simdjson/dom/serialization.h" */
/* begin file simdjson/dom/serialization.h */
#ifndef SIMDJSON_SERIALIZATION_H
#define SIMDJSON_SERIALIZATION_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/element.h" */
/* skipped duplicate #include "simdjson/dom/object.h" */
namespace simdjson {
/**
* The string_builder template and mini_formatter class
* are not part of our public API and are subject to change
* at any time!
*/
namespace internal {
template <class formatter> class base_formatter {
public:
/** Add a comma **/
simdjson_inline void comma();
/** Start an array, prints [ **/
simdjson_inline void start_array();
/** End an array, prints ] **/
simdjson_inline void end_array();
/** Start an array, prints { **/
simdjson_inline void start_object();
/** Start an array, prints } **/
simdjson_inline void end_object();
/** Prints a true **/
simdjson_inline void true_atom();
/** Prints a false **/
simdjson_inline void false_atom();
/** Prints a null **/
simdjson_inline void null_atom();
/** Prints a number **/
simdjson_inline void number(int64_t x);
/** Prints a number **/
simdjson_inline void number(uint64_t x);
/** Prints a number **/
simdjson_inline void number(double x);
/** Prints a key (string + colon) **/
simdjson_inline void key(std::string_view unescaped);
/** Prints a string. The string is escaped as needed. **/
simdjson_inline void string(std::string_view unescaped);
/** Clears out the content. **/
simdjson_inline void clear();
/**
* Get access to the buffer, it is owned by the instance, but
* the user can make a copy.
**/
simdjson_inline std::string_view str() const;
/** Prints one character **/
simdjson_inline void one_char(char c);
/** Prints characters in [begin, end) verbatim. **/
simdjson_inline void chars(const char *begin, const char *end);
simdjson_inline void call_print_newline() {
static_cast<formatter *>(this)->print_newline();
}
simdjson_inline void call_print_indents(size_t depth) {
static_cast<formatter *>(this)->print_indents(depth);
}
simdjson_inline void call_print_space() {
static_cast<formatter *>(this)->print_space();
}
protected:
// implementation details (subject to change)
/** Backing buffer **/
struct vector_with_small_buffer {
vector_with_small_buffer() = default;
~vector_with_small_buffer() { free_buffer(); }
vector_with_small_buffer(const vector_with_small_buffer &) = delete;
vector_with_small_buffer &
operator=(const vector_with_small_buffer &) = delete;
void clear() {
size = 0;
capacity = StaticCapacity;
free_buffer();
buffer = array;
}
simdjson_inline void push_back(char c) {
if (capacity < size + 1)
grow(capacity * 2);
buffer[size++] = c;
}
simdjson_inline void append(const char *begin, const char *end) {
const size_t new_size = size + (end - begin);
if (capacity < new_size)
// std::max(new_size, capacity * 2); is broken in tests on Windows
grow(new_size < capacity * 2 ? capacity * 2 : new_size);
std::copy(begin, end, buffer + size);
size = new_size;
}
std::string_view str() const { return std::string_view(buffer, size); }
private:
void free_buffer() {
if (buffer != array)
delete[] buffer;
}
void grow(size_t new_capacity) {
auto new_buffer = new char[new_capacity];
std::copy(buffer, buffer + size, new_buffer);
free_buffer();
buffer = new_buffer;
capacity = new_capacity;
}
static const size_t StaticCapacity = 64;
char array[StaticCapacity];
char *buffer = array;
size_t size = 0;
size_t capacity = StaticCapacity;
} buffer{};
};
/**
* @private This is the class that we expect to use with the string_builder
* template. It tries to produce a compact version of the JSON element
* as quickly as possible.
*/
class mini_formatter : public base_formatter<mini_formatter> {
public:
simdjson_inline void print_newline();
simdjson_inline void print_indents(size_t depth);
simdjson_inline void print_space();
};
class pretty_formatter : public base_formatter<pretty_formatter> {
public:
simdjson_inline void print_newline();
simdjson_inline void print_indents(size_t depth);
simdjson_inline void print_space();
protected:
int indent_step = 4;
};
/**
* @private The string_builder template allows us to construct
* a string from a document element. It is parametrized
* by a "formatter" which handles the details. Thus
* the string_builder template could support both minification
* and prettification, and various other tradeoffs.
*
* This is not to be confused with the simdjson::builder::string_builder
* which is a different class.
*/
template <class formatter = mini_formatter> class string_builder {
public:
/** Construct an initially empty builder, would print the empty string **/
string_builder() = default;
/** Append an element to the builder (to be printed) **/
inline void append(simdjson::dom::element value);
/** Append an array to the builder (to be printed) **/
inline void append(simdjson::dom::array value);
/** Append an object to the builder (to be printed) **/
inline void append(simdjson::dom::object value);
/** Reset the builder (so that it would print the empty string) **/
simdjson_inline void clear();
/**
* Get access to the string. The string_view is owned by the builder
* and it is invalid to use it after the string_builder has been
* destroyed.
* However you can make a copy of the string_view on memory that you
* own.
*/
simdjson_inline std::string_view str() const;
/** Append a key_value_pair to the builder (to be printed) **/
simdjson_inline void append(simdjson::dom::key_value_pair value);
private:
formatter format{};
};
} // namespace internal
namespace dom {
/**
* Print JSON to an output stream.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson
* itself will not throw.
*/
inline std::ostream &operator<<(std::ostream &out,
simdjson::dom::element value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream &
operator<<(std::ostream &out,
simdjson::simdjson_result<simdjson::dom::element> x);
#endif
/**
* Print JSON to an output stream.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson
* itself will not throw.
*/
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream &
operator<<(std::ostream &out,
simdjson::simdjson_result<simdjson::dom::array> x);
#endif
/**
* Print JSON to an output stream.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson
* itself will not throw.
*/
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream &
operator<<(std::ostream &out,
simdjson::simdjson_result<simdjson::dom::object> x);
#endif
} // namespace dom
/**
* Converts JSON to a string.
*
* dom::parser parser;
* element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
* cout << to_string(doc) << endl; // prints [1,2,3]
*
*/
template <class T> std::string to_string(T x) {
// in C++, to_string is standard:
// http://www.cplusplus.com/reference/string/to_string/ Currently minify and
// to_string are identical but in the future, they may differ.
simdjson::internal::string_builder<> sb;
sb.append(x);
std::string_view answer = sb.str();
return std::string(answer.data(), answer.size());
}
#if SIMDJSON_EXCEPTIONS
template <class T> std::string to_string(simdjson_result<T> x) {
if (x.error()) {
throw simdjson_error(x.error());
}
return to_string(x.value());
}
#endif
/**
* Minifies a JSON element or document, printing the smallest possible valid
* JSON.
*
* dom::parser parser;
* element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
* cout << minify(doc) << endl; // prints [1,2,3]
*
*/
template <class T> std::string minify(T x) { return to_string(x); }
#if SIMDJSON_EXCEPTIONS
template <class T> std::string minify(simdjson_result<T> x) {
if (x.error()) {
throw simdjson_error(x.error());
}
return to_string(x.value());
}
#endif
/**
* Prettifies a JSON element or document, printing the valid JSON with
* indentation.
*
* dom::parser parser;
* element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
*
* // Prints:
* // {
* // [
* // 1,
* // 2,
* // 3
* // ]
* // }
* cout << prettify(doc) << endl;
*
*/
template <class T> std::string prettify(T x) {
simdjson::internal::string_builder<simdjson::internal::pretty_formatter> sb;
sb.append(x);
std::string_view answer = sb.str();
return std::string(answer.data(), answer.size());
}
#if SIMDJSON_EXCEPTIONS
template <class T> std::string prettify(simdjson_result<T> x) {
if (x.error()) {
throw simdjson_error(x.error());
}
return prettify(x.value());
}
#endif
} // namespace simdjson
#endif
/* end file simdjson/dom/serialization.h */
/* including simdjson/dom/fractured_json.h: #include "simdjson/dom/fractured_json.h" */
/* begin file simdjson/dom/fractured_json.h */
#ifndef SIMDJSON_DOM_FRACTURED_JSON_H
#define SIMDJSON_DOM_FRACTURED_JSON_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/element.h" */
namespace simdjson {
/** Specifies where commas should be in table-formatted elements. */
enum class table_comma_placement {
/** Commas come right after the value */
before_padding,
/** Commas come after the column padding, so they line up in their own column. */
after_padding,
/** Commas come right after the value, except for columns of numbers */
before_padding_except_numbers,
};
/** Options for how lists or columns of numbers should be aligned */
enum class number_list_alignment {
/** Left-aligns numbers */
left,
/** Right-aligns numbers */
right,
};
/**
* Configuration options for FracturedJson formatting.
*
* FracturedJson intelligently chooses between different layout strategies
* (inline, compact multiline, table, expanded) based on content complexity,
* length, and structure similarity.
*/
struct fractured_json_options {
/**
* Maximum total characters per line (default: 120).
* Content exceeding this will be expanded to multiple lines.
*/
size_t max_total_line_length = 120;
/**
* Maximum nesting depth for inline rendering (default: 2).
* Elements with complexity exceeding this will be expanded.
* Complexity 0 = scalar, 1 = flat array/object, 2 = one level of nesting.
*/
size_t max_inline_complexity = 2;
/**
* Maximum complexity for compact array formatting (default: 2).
* Arrays with elements of this complexity or less may have multiple
* items per line.
*/
size_t max_compact_array_complexity = 2;
/**
* Number of spaces per indentation level (default: 4).
*/
size_t indent_spaces = 4;
/**
* Forces elements close to the root to always fully expand, regardless of other settings.
* (default: -1). -1 = none; 0 = root node only; 1 = root node and its children; etc.
*/
int always_expand_depth = -1;
/**
* Enable tabular formatting for arrays of similar objects or arrays
* (default: true). When enabled, the rows of such an array are written one
* per line with their columns aligned. Rows need not have identical keys:
* columns are ordered by the first occurrence of each key, and a row
* missing a key gets blank space in that column.
*/
bool enable_table_format = true;
/**
* Maximum complexity of each row of a table (default: 2).
* 0 = rows may only be scalars (a single column); 1 = rows may be flat
* arrays/objects; higher values allow deeper nesting.
*/
size_t max_table_row_complexity = 2;
/**
* Enable compact multiline arrays (default: true).
* When enabled, arrays of simple elements may have multiple items
* per line.
*/
bool enable_compact_multiline = true;
/**
* Minimum number of items per line for an array to be formatted as a
* compact multiline array (default: 3).
*/
size_t min_compact_array_row_items = 3;
/**
* Add space inside brackets for containers that hold only scalar values
* (default: false). When true: { "key": "value" }. When false:
* {"key": "value"}.
* @see nested_bracket_padding
*/
bool simple_bracket_padding = false;
/**
* Add space inside brackets for containers that hold at least one
* nested array/object (default: true). When true: { "a": [1, 2] }.
* When false: {"a": [1, 2]}.
* @see simple_bracket_padding
*/
bool nested_bracket_padding = true;
/**
* Add space after colons (default: true).
* When true: "key": "value"
* When false: "key":"value"
*/
bool colon_padding = true;
/**
* Add space after commas in inline content (default: true).
* When true: [1, 2, 3]
* When false: [1,2,3]
*/
bool comma_padding = true;
/**
* Placement of commas relative to column padding in table-formatted rows
* and compact multiline arrays (default: before_padding_except_numbers).
*/
table_comma_placement comma_placement = table_comma_placement::before_padding_except_numbers;
/**
* Controls alignment of numbers in table columns or compact multiline arrays
* (default: left). Numbers are always written exactly as in the input.
*/
number_list_alignment number_alignment = number_list_alignment::left;
};
/**
* Format JSON using FracturedJson formatting with default options.
*
* FracturedJson produces human-readable yet compact output by intelligently
* choosing between inline, compact multiline, table, and expanded layouts.
*
* dom::parser parser;
* element doc = parser.parse(json_string);
* cout << fractured_json(doc) << endl;
*/
template <class T>
std::string fractured_json(T x);
/**
* Format JSON using FracturedJson formatting with custom options.
*
* dom::parser parser;
* element doc = parser.parse(json_string);
* fractured_json_options opts;
* opts.max_total_line_length = 80;
* cout << fractured_json(doc, opts) << endl;
*/
template <class T>
std::string fractured_json(T x, const fractured_json_options& options);
#if SIMDJSON_EXCEPTIONS
template <class T>
std::string fractured_json(simdjson_result<T> x);
template <class T>
std::string fractured_json(simdjson_result<T> x, const fractured_json_options& options);
#endif
/**
* Format a JSON string using FracturedJson formatting.
*
* This is useful for formatting output from the builder/static reflection API
* or any valid JSON string.
*
* // With static reflection
* MyStruct data = {...};
* auto minified = simdjson::to_json_string(data);
* auto formatted = simdjson::fractured_json_string(minified.value());
*
* // Or with any JSON string
* std::string json = R"({"key":"value"})";
* auto formatted = simdjson::fractured_json_string(json);
*/
inline std::string fractured_json_string(std::string_view json_str);
/**
* Format a JSON string using FracturedJson formatting with custom options.
*/
inline std::string fractured_json_string(std::string_view json_str,
const fractured_json_options& options);
} // namespace simdjson
#endif // SIMDJSON_DOM_FRACTURED_JSON_H
/* end file simdjson/dom/fractured_json.h */
// Inline functions
/* including simdjson/dom/array-inl.h: #include "simdjson/dom/array-inl.h" */
/* begin file simdjson/dom/array-inl.h */
#ifndef SIMDJSON_ARRAY_INL_H
#define SIMDJSON_ARRAY_INL_H
#include <utility>
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/array.h" */
/* skipped duplicate #include "simdjson/dom/element.h" */
/* skipped duplicate #include "simdjson/error-inl.h" */
/* including simdjson/jsonpathutil.h: #include "simdjson/jsonpathutil.h" */
/* begin file simdjson/jsonpathutil.h */
#ifndef SIMDJSON_JSONPATHUTIL_H
#define SIMDJSON_JSONPATHUTIL_H
/* skipped duplicate #include "simdjson/error.h" */
#include <string>
/* skipped duplicate #include "simdjson/common_defs.h" */
#include <limits>
#include <utility>
#include <vector>
namespace simdjson {
namespace internal {
/**
* Parses the next JSON Pointer array index token.
*
* The caller passes a pointer fragment with no leading '/', such as "123/foo".
* On success, array_index receives the parsed index and token_length receives
* the number of bytes consumed before the next '/' or the end of the fragment.
*/
simdjson_inline error_code parse_json_pointer_array_index(std::string_view json_pointer,
size_t &array_index,
size_t &token_length) noexcept {
array_index = 0;
token_length = 0;
for (; token_length < json_pointer.length() && json_pointer[token_length] != '/';
token_length++) {
uint8_t digit = uint8_t(json_pointer[token_length] - '0');
// Check for non-digit in array index. If it's there, we're trying to get a field in an object.
if (digit > 9) {
return INCORRECT_TYPE;
}
// 0 followed by other digits is invalid.
if (token_length > 0 && json_pointer[0] == '0') {
return INVALID_JSON_POINTER;
}
if (array_index >
(((std::numeric_limits<size_t>::max)() - digit) / 10)) {
return INDEX_OUT_OF_BOUNDS;
}
array_index = array_index * 10 + digit;
}
// Empty string is invalid; so is a "/" with no digits before it.
if (token_length == 0) {
return INVALID_JSON_POINTER;
}
return SUCCESS;
}
/**
* Returns true if the error means that a JSONPath segment matched nothing
* (missing field, index out of bounds, value of the wrong type, or a scalar
* reached while path segments remain), as opposed to a document error.
* A wildcard walk skips such branches, as RFC 9535 and DOM's
* at_path_with_wildcard do.
*/
simdjson_inline bool is_matching_failure_error(error_code error) noexcept {
return error == NO_SUCH_FIELD || error == INDEX_OUT_OF_BOUNDS ||
error == INCORRECT_TYPE || error == INVALID_JSON_POINTER;
}
/**
* Returns true if a wildcard follows in the remaining JSONPath. A segment that
* matches nothing before a wildcard yields an empty result instead of an
* error, as in DOM's at_path_with_wildcard. Like DOM, this treats a '*' inside
* a quoted key as a wildcard.
*/
simdjson_inline bool json_path_has_wildcard(std::string_view json_path) noexcept {
return json_path.find('*') != std::string_view::npos;
}
} // namespace internal
/**
* Converts JSONPath to JSON Pointer.
* @param json_path The JSONPath string to be converted.
* @return A string containing the equivalent JSON Pointer.
*/
inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
size_t i = 0;
// if JSONPath starts with $, skip it
// json_path.starts_with('$') requires C++20.
if (!json_path.empty() && json_path.front() == '$') {
i = 1;
}
if (i >= json_path.size() || (json_path[i] != '.' &&
json_path[i] != '[')) {
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);
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 std::pair<std::string_view, std::string_view> get_next_key_and_json_path(std::string_view& json_path) {
std::string_view key;
if (json_path.empty()) {
return {key, json_path};
}
size_t i = 0;
// if JSONPath starts with $, skip it
if (json_path.front() == '$') {
i = 1;
}
if (i < json_path.length() && json_path[i] == '.') {
i += 1;
size_t key_start = i;
while (i < json_path.length() && json_path[i] != '[' && json_path[i] != '.') {
++i;
}
key = json_path.substr(key_start, i - key_start);
} else if ((i + 1 < json_path.size()) && json_path[i] == '[' &&
(json_path[i + 1] == '\'' || json_path[i + 1] == '"')) {
// Bracket-quoted key: ['key'] or ["key"].
// Require a matching closing quote and a following ']'. If either is
// missing, return an empty key and the original path so callers can treat
// this as a parse failure (e.g. INVALID_JSON_POINTER) without advancing.
// Without this check, i += 2 can make i > size() and substr throws
// std::out_of_range, which aborts noexcept callers such as
// at_path_with_wildcard / for_each_at_path_with_wildcard.
const char quote = json_path[i + 1];
i += 2;
const size_t key_start = i;
while (i < json_path.length() && json_path[i] != quote) {
++i;
}
if (i >= json_path.length() || // missing closing quote
i + 1 >= json_path.length() || // missing ]
json_path[i + 1] != ']') {
return {key, json_path};
}
key = json_path.substr(key_start, i - key_start);
i += 2; // past quote and ]
} else if ((i+2 < json_path.size()) && json_path[i] == '[' && json_path[i+1] == '*' && json_path[i+2] == ']') { // i.e [*].additional_keys or [*]["additional_keys"]
key = "*";
i += 3;
} else if (i < json_path.size() && json_path[i] == '[') { // i.e. [0] or [123]
size_t j = i + 1;
while (j < json_path.size() && json_path[j] >= '0' && json_path[j] <= '9') {
++j;
}
if (j > i + 1 && j < json_path.size() && json_path[j] == ']') {
key = json_path.substr(i + 1, j - i - 1);
i = j + 1;
}
}
return std::make_pair(key, json_path.substr(i));
}
namespace internal {
/**
* A JSONPath as the DOM at_path_with_wildcard() consumes it, parsed once.
*
* Every node visited at a given depth sees the same remaining path, so we
* parse it up front rather than at each node: re-scanning (and converting)
* the remaining path at every node costs O(nodes * path length), which
* made a long path after a couple of wildcards take minutes on a large
* document.
*/
struct wildcard_json_path {
enum class tail_type { invalid, all_values, pointer };
// One JSON pointer ("/key") per leading segment, "/*" being a wildcard.
std::vector<std::string> steps{};
// What to do once the steps are exhausted.
tail_type tail{tail_type::invalid};
// The rest of the path, as a JSON pointer, when tail == pointer.
std::string tail_pointer{};
};
/**
* Splits json_path the same way the recursive DOM at_path_with_wildcard()
* historically did, one level at a time, but in a single pass.
*/
inline wildcard_json_path parse_wildcard_json_path(std::string_view json_path) {
wildcard_json_path result;
// The remaining path (always a suffix of json_path) contains a '*' if and
// only if it starts at or before the last one.
const size_t last_star = json_path.rfind('*');
std::string_view path = json_path;
while (true) {
size_t i = 0;
if (!path.empty() && path.front() == '$') {
i = 1;
}
if (i >= path.size() || (path[i] != '.' && path[i] != '[')) {
result.tail = wildcard_json_path::tail_type::invalid;
return result;
}
const size_t offset = json_path.size() - path.size();
if (last_star == std::string_view::npos || last_star < offset) {
result.tail_pointer = json_path_to_pointer_conversion(path);
result.tail = (result.tail_pointer == "-1")
? wildcard_json_path::tail_type::invalid
: wildcard_json_path::tail_type::pointer;
return result;
}
if ((path.compare(i, 3, "[*]") == 0 && path.size() == i + 3) ||
(path.compare(i, 2, ".*") == 0 && path.size() == i + 2)) {
result.tail = wildcard_json_path::tail_type::all_values;
return result;
}
std::pair<std::string_view, std::string_view> key_and_json_path = get_next_key_and_json_path(path);
if (key_and_json_path.first.empty()) {
result.tail = wildcard_json_path::tail_type::invalid;
return result;
}
std::string step;
step.reserve(key_and_json_path.first.size() + 1);
step += '/';
step.append(key_and_json_path.first.data(), key_and_json_path.first.size());
result.steps.push_back(std::move(step));
path = key_and_json_path.second;
}
}
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_JSONPATHUTIL_H
/* end file simdjson/jsonpathutil.h */
/* including simdjson/internal/tape_ref-inl.h: #include "simdjson/internal/tape_ref-inl.h" */
/* begin file simdjson/internal/tape_ref-inl.h */
#ifndef SIMDJSON_TAPE_REF_INL_H
#define SIMDJSON_TAPE_REF_INL_H
/* skipped duplicate #include "simdjson/dom/document.h" */
/* skipped duplicate #include "simdjson/internal/tape_ref.h" */
/* including simdjson/internal/tape_type.h: #include "simdjson/internal/tape_type.h" */
/* begin file simdjson/internal/tape_type.h */
#ifndef SIMDJSON_INTERNAL_TAPE_TYPE_H
#define SIMDJSON_INTERNAL_TAPE_TYPE_H
namespace simdjson {
namespace internal {
/**
* The possible types in the tape.
*/
enum class tape_type {
ROOT = 'r',
START_ARRAY = '[',
START_OBJECT = '{',
END_ARRAY = ']',
END_OBJECT = '}',
STRING = '"',
INT64 = 'l',
UINT64 = 'u',
DOUBLE = 'd',
TRUE_VALUE = 't',
FALSE_VALUE = 'f',
NULL_VALUE = 'n',
BIGINT = 'Z' // Big integer stored as string in string buffer
}; // enum class tape_type
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_TAPE_TYPE_H
/* end file simdjson/internal/tape_type.h */
#include <cstring>
namespace simdjson {
namespace internal {
constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF;
constexpr const uint32_t JSON_COUNT_MASK = 0xFFFFFF;
//
// tape_ref inline implementation
//
simdjson_inline tape_ref::tape_ref() noexcept : doc{nullptr}, json_index{0} {}
simdjson_inline tape_ref::tape_ref(const dom::document *_doc, size_t _json_index) noexcept : doc{_doc}, json_index{_json_index} {}
simdjson_inline bool tape_ref::is_document_root() const noexcept {
return json_index == 1; // should we ever change the structure of the tape, this should get updated.
}
simdjson_inline bool tape_ref::usable() const noexcept {
return doc != nullptr; // when the document pointer is null, this tape_ref is uninitialized (should not be accessed).
}
// Some value types have a specific on-tape word value. It can be faster
// to check the type by doing a word-to-word comparison instead of extracting the
// most significant 8 bits.
simdjson_inline bool tape_ref::is_double() const noexcept {
constexpr uint64_t tape_double = uint64_t(tape_type::DOUBLE)<<56;
return doc->tape[json_index] == tape_double;
}
simdjson_inline bool tape_ref::is_int64() const noexcept {
constexpr uint64_t tape_int64 = uint64_t(tape_type::INT64)<<56;
return doc->tape[json_index] == tape_int64;
}
simdjson_inline bool tape_ref::is_uint64() const noexcept {
constexpr uint64_t tape_uint64 = uint64_t(tape_type::UINT64)<<56;
return doc->tape[json_index] == tape_uint64;
}
simdjson_inline bool tape_ref::is_false() const noexcept {
constexpr uint64_t tape_false = uint64_t(tape_type::FALSE_VALUE)<<56;
return doc->tape[json_index] == tape_false;
}
simdjson_inline bool tape_ref::is_true() const noexcept {
constexpr uint64_t tape_true = uint64_t(tape_type::TRUE_VALUE)<<56;
return doc->tape[json_index] == tape_true;
}
simdjson_inline bool tape_ref::is_null_on_tape() const noexcept {
constexpr uint64_t tape_null = uint64_t(tape_type::NULL_VALUE)<<56;
return doc->tape[json_index] == tape_null;
}
inline size_t tape_ref::after_element() const noexcept {
switch (tape_ref_type()) {
case tape_type::START_ARRAY:
case tape_type::START_OBJECT:
return matching_brace_index();
case tape_type::UINT64:
case tape_type::INT64:
case tape_type::DOUBLE:
return json_index + 2;
default:
return json_index + 1;
}
}
simdjson_inline tape_type tape_ref::tape_ref_type() const noexcept {
return static_cast<tape_type>(doc->tape[json_index] >> 56);
}
simdjson_inline size_t tape_ref::before_element(size_t array_start) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(usable());
SIMDJSON_DEVELOPMENT_ASSERT(json_index > array_start);
tape_ref previous(doc, json_index - 1);
if (previous.json_index == array_start) { return array_start; }
// An exact numeric marker cannot end an element unless it is itself the
// payload of a number. In that case its header is immediately before it.
if (previous.is_int64() || previous.is_uint64() || previous.is_double()) {
return previous.json_index - 1;
}
tape_ref probe(doc, previous.json_index - 1);
// Validate both container links before examining its contents. A candidate
// opening tag preceded by an even run of numeric markers is a real tag,
// not a numeric payload. Scan both candidate boundaries together so that a
// forged opening tag inside a nested value cannot cause an unbounded detour.
// For a real container, the opening probe visits preceding siblings. For a
// numeric payload, the other probe does. Stopping at the shorter run bounds
// the work by the array's immediate elements, rather than nested contents.
const auto type = previous.tape_ref_type();
if (type == tape_type::END_ARRAY || type == tape_type::END_OBJECT) {
const size_t start = previous.matching_brace_index();
if (start > array_start && start < previous.json_index) {
tape_ref opening(doc, start);
const auto expected = type == tape_type::END_ARRAY
? tape_type::START_ARRAY : tape_type::START_OBJECT;
if (opening.tape_ref_type() == expected &&
opening.matching_brace_index() == previous.json_index + 1) {
tape_ref before_opening(doc, start - 1);
while ((before_opening.is_int64() || before_opening.is_uint64() ||
before_opening.is_double()) &&
(probe.is_int64() || probe.is_uint64() || probe.is_double())) {
--before_opening.json_index;
--probe.json_index;
}
if (!before_opening.is_int64() && !before_opening.is_uint64() &&
!before_opening.is_double() &&
(start - before_opening.json_index) % 2 == 1) {
return start;
}
}
}
}
// Numeric payloads can have ANY bit pattern, including another type's tag.
// A run of exact numeric markers starts with a header, then alternates
// between payload and header. An odd run before this word makes it a payload.
// Subsequent reverse increments through exact-marker payloads take the
// constant-time numeric-payload branch above.
while (probe.is_int64() || probe.is_uint64() || probe.is_double()) {
--probe.json_index;
}
if ((previous.json_index - probe.json_index) % 2 == 0) {
return previous.json_index - 1;
}
// Once distinguished from numeric payloads, closing container tags link
// directly back to their opening tags.
switch (previous.tape_ref_type()) {
case tape_type::END_ARRAY:
case tape_type::END_OBJECT:
return previous.matching_brace_index();
default:
return previous.json_index;
}
}
simdjson_inline uint64_t internal::tape_ref::tape_value() const noexcept {
return doc->tape[json_index] & internal::JSON_VALUE_MASK;
}
simdjson_inline uint32_t internal::tape_ref::matching_brace_index() const noexcept {
return uint32_t(doc->tape[json_index]);
}
simdjson_inline uint32_t internal::tape_ref::scope_count() const noexcept {
return uint32_t((doc->tape[json_index] >> 32) & internal::JSON_COUNT_MASK);
}
template<typename T>
simdjson_inline T tape_ref::next_tape_value() const noexcept {
static_assert(sizeof(T) == sizeof(uint64_t), "next_tape_value() template parameter must be 64-bit");
// Though the following is tempting...
// return *reinterpret_cast<const T*>(&doc->tape[json_index + 1]);
// It is not generally safe. It is safer, and often faster to rely
// on memcpy. Yes, it is uglier, but it is also encapsulated.
T x;
std::memcpy(&x,&doc->tape[json_index + 1],sizeof(uint64_t));
return x;
}
simdjson_inline uint32_t internal::tape_ref::get_string_length() const noexcept {
size_t string_buf_index = size_t(tape_value());
uint32_t len;
std::memcpy(&len, &doc->string_buf[string_buf_index], sizeof(len));
return len;
}
simdjson_inline const char * internal::tape_ref::get_c_str() const noexcept {
size_t string_buf_index = size_t(tape_value());
return reinterpret_cast<const char *>(&doc->string_buf[string_buf_index + sizeof(uint32_t)]);
}
inline std::string_view internal::tape_ref::get_string_view() const noexcept {
return std::string_view(
get_c_str(),
get_string_length()
);
}
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_TAPE_REF_INL_H
/* end file simdjson/internal/tape_ref-inl.h */
#include <limits>
namespace simdjson {
//
// simdjson_result<dom::array> inline implementation
//
simdjson_inline simdjson_result<dom::array>::simdjson_result() noexcept
: internal::simdjson_result_base<dom::array>() {}
simdjson_inline simdjson_result<dom::array>::simdjson_result(dom::array value) noexcept
: internal::simdjson_result_base<dom::array>(std::forward<dom::array>(value)) {}
simdjson_inline simdjson_result<dom::array>::simdjson_result(error_code error) noexcept
: internal::simdjson_result_base<dom::array>(error) {}
#if SIMDJSON_EXCEPTIONS
inline dom::array::iterator simdjson_result<dom::array>::begin() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.begin();
}
inline dom::array::iterator simdjson_result<dom::array>::end() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.end();
}
inline size_t simdjson_result<dom::array>::size() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.size();
}
inline dom::array::reverse_iterator simdjson_result<dom::array>::rbegin() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.rbegin();
}
inline dom::array::reverse_iterator simdjson_result<dom::array>::rend() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.rend();
}
#endif // SIMDJSON_EXCEPTIONS
inline simdjson_result<dom::element> simdjson_result<dom::array>::at_pointer(std::string_view json_pointer) const noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
inline simdjson_result<dom::element> simdjson_result<dom::array>::at_path(std::string_view json_path) const noexcept {
if (error()) { return error(); }
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::array>::at_path_with_wildcard(std::string_view json_path) const noexcept {
if (error()) {
return error();
}
return first.at_path_with_wildcard(json_path);
}
inline simdjson_result<dom::element> simdjson_result<dom::array>::at(size_t index) const noexcept {
if (error()) { return error(); }
return first.at(index);
}
inline std::vector<dom::element>& simdjson_result<dom::array>::get_values(std::vector<dom::element>& out) const noexcept {
return first.get_values(out);
}
namespace dom {
//
// array inline implementation
//
simdjson_inline array::array() noexcept : tape{} {}
simdjson_inline array::array(const internal::tape_ref &_tape) noexcept : tape{_tape} {}
inline array::iterator array::begin() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return internal::tape_ref(tape.doc, tape.json_index + 1);
}
inline array::iterator array::end() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return internal::tape_ref(tape.doc, tape.after_element() - 1);
}
inline size_t array::size() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return tape.scope_count();
}
inline array::reverse_iterator array::rbegin() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable());
const internal::tape_ref end_tape(tape.doc, tape.matching_brace_index() - 1);
return reverse_iterator(internal::tape_ref(tape.doc, end_tape.before_element(tape.json_index)), tape.json_index);
}
inline array::reverse_iterator array::rend() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable());
return reverse_iterator(tape, tape.json_index);
}
inline size_t array::number_of_slots() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return tape.matching_brace_index() - tape.json_index;
}
inline simdjson_result<element> array::at_pointer(std::string_view json_pointer) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(json_pointer.empty()) { // an empty string means that we return the current node
return element(this->tape); // copy the current node
} else if(json_pointer[0] != '/') { // otherwise there is an error
return INVALID_JSON_POINTER;
}
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = array(tape).at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<element> array::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline void array::process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept {
if (current == end) {
return;
}
simdjson_result<std::vector<element>> result;
for (auto it = current; it != end; ++it) {
std::vector<element> child_result;
auto error = it->at_path_with_wildcard(path_suffix).get(child_result);
if(error) {
continue;
}
accumulator.insert(accumulator.end(),
std::make_move_iterator(child_result.begin()),
std::make_move_iterator(child_result.end()));
}
}
inline simdjson_result<std::vector<element>> array::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return element(*this).at_path_with_wildcard(json_path);
}
inline simdjson_result<element> array::at(size_t index) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
size_t i=0;
for (auto element : *this) {
if (i == index) { return element; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
inline std::vector<element>& array::get_values(std::vector<element>& out) const noexcept {
out.reserve(this->size());
for (auto element : *this) {
out.emplace_back(element);
}
return out;
}
inline array::operator element() const noexcept {
return element(tape);
}
//
// array::reverse_iterator inline implementation
//
simdjson_inline array::reverse_iterator::reverse_iterator(const internal::tape_ref &_tape, size_t _array_start) noexcept
: tape{_tape}, array_start{_array_start} { }
inline element array::reverse_iterator::operator*() const noexcept {
return element(tape);
}
inline array::reverse_iterator& array::reverse_iterator::operator++() noexcept {
tape.json_index = tape.before_element(array_start);
return *this;
}
inline array::reverse_iterator array::reverse_iterator::operator++(int) noexcept {
reverse_iterator out = *this;
++*this;
return out;
}
inline bool array::reverse_iterator::operator==(const reverse_iterator& other) const noexcept {
return tape.doc == other.tape.doc && tape.json_index == other.tape.json_index;
}
inline bool array::reverse_iterator::operator!=(const reverse_iterator& other) const noexcept {
return !(*this == other);
}
//
// array::iterator inline implementation
//
simdjson_inline array::iterator::iterator(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
inline element array::iterator::operator*() const noexcept {
return element(tape);
}
inline array::iterator& array::iterator::operator++() noexcept {
tape.json_index = tape.after_element();
return *this;
}
inline array::iterator array::iterator::operator++(int) noexcept {
array::iterator out = *this;
++*this;
return out;
}
inline bool array::iterator::operator!=(const array::iterator& other) const noexcept {
return tape.json_index != other.tape.json_index;
}
inline bool array::iterator::operator==(const array::iterator& other) const noexcept {
return tape.json_index == other.tape.json_index;
}
inline bool array::iterator::operator<(const array::iterator& other) const noexcept {
return tape.json_index < other.tape.json_index;
}
inline bool array::iterator::operator<=(const array::iterator& other) const noexcept {
return tape.json_index <= other.tape.json_index;
}
inline bool array::iterator::operator>=(const array::iterator& other) const noexcept {
return tape.json_index >= other.tape.json_index;
}
inline bool array::iterator::operator>(const array::iterator& other) const noexcept {
return tape.json_index > other.tape.json_index;
}
} // namespace dom
} // namespace simdjson
/* including simdjson/dom/element-inl.h: #include "simdjson/dom/element-inl.h" */
/* begin file simdjson/dom/element-inl.h */
#ifndef SIMDJSON_ELEMENT_INL_H
#define SIMDJSON_ELEMENT_INL_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/element.h" */
/* skipped duplicate #include "simdjson/dom/document.h" */
/* skipped duplicate #include "simdjson/dom/object.h" */
/* skipped duplicate #include "simdjson/internal/tape_type.h" */
/* including simdjson/dom/object-inl.h: #include "simdjson/dom/object-inl.h" */
/* begin file simdjson/dom/object-inl.h */
#ifndef SIMDJSON_OBJECT_INL_H
#define SIMDJSON_OBJECT_INL_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/object.h" */
/* skipped duplicate #include "simdjson/dom/document.h" */
/* skipped duplicate #include "simdjson/dom/element-inl.h" */
/* skipped duplicate #include "simdjson/error-inl.h" */
/* skipped duplicate #include "simdjson/jsonpathutil.h" */
#include <cstring>
namespace simdjson {
//
// simdjson_result<dom::object> inline implementation
//
simdjson_inline simdjson_result<dom::object>::simdjson_result() noexcept
: internal::simdjson_result_base<dom::object>() {}
simdjson_inline simdjson_result<dom::object>::simdjson_result(dom::object value) noexcept
: internal::simdjson_result_base<dom::object>(std::forward<dom::object>(value)) {}
simdjson_inline simdjson_result<dom::object>::simdjson_result(error_code error) noexcept
: internal::simdjson_result_base<dom::object>(error) {}
inline simdjson_result<dom::element> simdjson_result<dom::object>::operator[](std::string_view key) const noexcept {
if (error()) { return error(); }
return first[key];
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::operator[](const char *key) const noexcept {
if (error()) { return error(); }
return first[key];
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_pointer(std::string_view json_pointer) const noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_path(std::string_view json_path) const noexcept {
if (error()) { return error(); }
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline simdjson_result<std::vector<dom::element>> simdjson_result<dom::object>::at_path_with_wildcard(std::string_view json_path) const noexcept {
if (error()) {
return error();
}
return first.at_path_with_wildcard(json_path);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key(std::string_view key) const noexcept {
if (error()) { return error(); }
return first.at_key(key);
}
inline std::vector<dom::element>& simdjson_result<dom::object>::get_values(std::vector<dom::element>& out) const noexcept {
return first.get_values(out);
}
inline simdjson_result<dom::element> simdjson_result<dom::object>::at_key_case_insensitive(std::string_view key) const noexcept {
if (error()) { return error(); }
return first.at_key_case_insensitive(key);
}
#if SIMDJSON_EXCEPTIONS
inline dom::object::iterator simdjson_result<dom::object>::begin() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.begin();
}
inline dom::object::iterator simdjson_result<dom::object>::end() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.end();
}
inline size_t simdjson_result<dom::object>::size() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.size();
}
#endif // SIMDJSON_EXCEPTIONS
namespace dom {
//
// object inline implementation
//
simdjson_inline object::object() noexcept : tape{} {}
simdjson_inline object::object(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
inline object::iterator object::begin() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return internal::tape_ref(tape.doc, tape.json_index + 1);
}
inline object::iterator object::end() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return internal::tape_ref(tape.doc, tape.after_element() - 1);
}
inline size_t object::size() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return tape.scope_count();
}
inline simdjson_result<element> object::operator[](std::string_view key) const noexcept {
return at_key(key);
}
inline simdjson_result<element> object::operator[](const char *key) const noexcept {
return at_key(key);
}
inline simdjson_result<element> object::at_pointer(std::string_view json_pointer) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(json_pointer.empty()) { // an empty string means that we return the current node
return element(this->tape); // copy the current node
} else if(json_pointer[0] != '/') { // otherwise there is an error
return INVALID_JSON_POINTER;
}
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<element> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = at_key(unescaped);
} else {
child = at_key(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<element> object::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
inline void object::process_json_path_of_child_elements(std::vector<element>::iterator& current, std::vector<element>::iterator& end, const std::string_view& path_suffix, std::vector<element>& accumulator) const noexcept {
if (current == end) {
return;
}
simdjson_result<std::vector<element>> result;
for (auto it = current; it != end; ++it) {
std::vector<element> child_result;
auto error = it->at_path_with_wildcard(path_suffix).get(child_result);
if(error) {
continue;
}
accumulator.insert(accumulator.end(),
std::make_move_iterator(child_result.begin()),
std::make_move_iterator(child_result.end()));
}
}
inline simdjson_result<std::vector<element>> object::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return element(*this).at_path_with_wildcard(json_path);
}
inline simdjson_result<element> object::at_key(std::string_view key) const noexcept {
iterator end_field = end();
for (iterator field = begin(); field != end_field; ++field) {
if (field.key_equals(key)) {
return field.value();
}
}
return NO_SUCH_FIELD;
}
inline std::vector<element>& object::get_values(std::vector<element>& out) const noexcept {
iterator end_field = end();
iterator begin_field = begin();
out.reserve(std::distance(begin_field, end_field));
for (iterator field = begin_field; field != end_field; ++field) {
out.emplace_back(field.value());
}
return out;
}
// In case you wonder why we need this, please see
// https://github.com/simdjson/simdjson/issues/323
// People do seek keys in a case-insensitive manner.
inline simdjson_result<element> object::at_key_case_insensitive(std::string_view key) const noexcept {
iterator end_field = end();
for (iterator field = begin(); field != end_field; ++field) {
if (field.key_equals_case_insensitive(key)) {
return field.value();
}
}
return NO_SUCH_FIELD;
}
inline object::operator element() const noexcept {
return element(tape);
}
//
// object::iterator inline implementation
//
simdjson_inline object::iterator::iterator(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
inline const key_value_pair object::iterator::operator*() const noexcept {
return key_value_pair(key(), value());
}
inline bool object::iterator::operator!=(const object::iterator& other) const noexcept {
return tape.json_index != other.tape.json_index;
}
inline bool object::iterator::operator==(const object::iterator& other) const noexcept {
return tape.json_index == other.tape.json_index;
}
inline bool object::iterator::operator<(const object::iterator& other) const noexcept {
return tape.json_index < other.tape.json_index;
}
inline bool object::iterator::operator<=(const object::iterator& other) const noexcept {
return tape.json_index <= other.tape.json_index;
}
inline bool object::iterator::operator>=(const object::iterator& other) const noexcept {
return tape.json_index >= other.tape.json_index;
}
inline bool object::iterator::operator>(const object::iterator& other) const noexcept {
return tape.json_index > other.tape.json_index;
}
inline object::iterator& object::iterator::operator++() noexcept {
tape.json_index++;
tape.json_index = tape.after_element();
return *this;
}
inline object::iterator object::iterator::operator++(int) noexcept {
object::iterator out = *this;
++*this;
return out;
}
inline std::string_view object::iterator::key() const noexcept {
return tape.get_string_view();
}
inline uint32_t object::iterator::key_length() const noexcept {
return tape.get_string_length();
}
inline const char* object::iterator::key_c_str() const noexcept {
return reinterpret_cast<const char *>(&tape.doc->string_buf[size_t(tape.tape_value()) + sizeof(uint32_t)]);
}
inline element object::iterator::value() const noexcept {
return element(internal::tape_ref(tape.doc, tape.json_index + 1));
}
/**
* Design notes:
* Instead of constructing a string_view and then comparing it with a
* user-provided strings, it is probably more performant to have dedicated
* functions taking as a parameter the string we want to compare against
* and return true when they are equal. That avoids the creation of a temporary
* std::string_view. Though it is possible for the compiler to avoid entirely
* any overhead due to string_view, relying too much on compiler magic is
* problematic: compiler magic sometimes fail, and then what do you do?
* Also, enticing users to rely on high-performance function is probably better
* on the long run.
*/
inline bool object::iterator::key_equals(std::string_view o) const noexcept {
// We use the fact that the key length can be computed quickly
// without access to the string buffer.
const uint32_t len = key_length();
if(o.size() == len) {
// We avoid construction of a temporary string_view instance.
return (memcmp(o.data(), key_c_str(), len) == 0);
}
return false;
}
inline bool object::iterator::key_equals_case_insensitive(std::string_view o) const noexcept {
// We use the fact that the key length can be computed quickly
// without access to the string buffer.
const uint32_t len = key_length();
if(o.size() == len) {
// See For case-insensitive string comparisons, avoid char-by-char functions
// https://lemire.me/blog/2020/04/30/for-case-insensitive-string-comparisons-avoid-char-by-char-functions/
// Note that it might be worth rolling our own strncasecmp function, with vectorization.
return (simdjson_strncasecmp(o.data(), key_c_str(), len) == 0);
}
return false;
}
//
// key_value_pair inline implementation
//
inline key_value_pair::key_value_pair(std::string_view _key, element _value) noexcept :
key(_key), value(_value) {}
} // namespace dom
} // namespace simdjson
#if SIMDJSON_SUPPORTS_RANGES
static_assert(std::ranges::view<simdjson::dom::object>);
static_assert(std::ranges::sized_range<simdjson::dom::object>);
#if SIMDJSON_EXCEPTIONS
static_assert(std::ranges::view<simdjson::simdjson_result<simdjson::dom::object>>);
static_assert(std::ranges::sized_range<simdjson::simdjson_result<simdjson::dom::object>>);
#endif // SIMDJSON_EXCEPTIONS
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_OBJECT_INL_H
/* end file simdjson/dom/object-inl.h */
/* skipped duplicate #include "simdjson/error-inl.h" */
/* skipped duplicate #include "simdjson/jsonpathutil.h" */
#include <ostream>
#include <limits>
namespace simdjson {
//
// simdjson_result<dom::element> inline implementation
//
simdjson_inline simdjson_result<dom::element>::simdjson_result() noexcept
: internal::simdjson_result_base<dom::element>() {}
simdjson_inline simdjson_result<dom::element>::simdjson_result(dom::element &&value) noexcept
: internal::simdjson_result_base<dom::element>(std::forward<dom::element>(value)) {}
simdjson_inline simdjson_result<dom::element>::simdjson_result(error_code error) noexcept
: internal::simdjson_result_base<dom::element>(error) {}
inline simdjson_result<dom::element_type> simdjson_result<dom::element>::type() const noexcept {
if (error()) { return error(); }
return first.type();
}
template<typename T>
simdjson_inline bool simdjson_result<dom::element>::is() const noexcept {
return !error() && first.is<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<dom::element>::get() const noexcept {
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<dom::element>::get(T &value) const noexcept {
if (error()) { return error(); }
return first.get<T>(value);
}
simdjson_inline simdjson_result<dom::array> simdjson_result<dom::element>::get_array() const noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<dom::object> simdjson_result<dom::element>::get_object() const noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<const char *> simdjson_result<dom::element>::get_c_str() const noexcept {
if (error()) { return error(); }
return first.get_c_str();
}
simdjson_inline simdjson_result<size_t> simdjson_result<dom::element>::get_string_length() const noexcept {
if (error()) { return error(); }
return first.get_string_length();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<dom::element>::get_string() const noexcept {
if (error()) { return error(); }
return first.get_string();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<dom::element>::get_u8string() const noexcept {
if (error()) { return error(); }
return first.get_u8string();
}
#endif
simdjson_inline simdjson_result<int64_t> simdjson_result<dom::element>::get_int64() const noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<dom::element>::get_uint64() const noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<double> simdjson_result<dom::element>::get_double() const noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<bool> simdjson_result<dom::element>::get_bool() const noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<dom::element>::get_bigint() const noexcept {
if (error()) { return error(); }
return first.get_bigint();
}
simdjson_inline bool simdjson_result<dom::element>::is_array() const noexcept {
return !error() && first.is_array();
}
simdjson_inline bool simdjson_result<dom::element>::is_object() const noexcept {
return !error() && first.is_object();
}
simdjson_inline bool simdjson_result<dom::element>::is_string() const noexcept {
return !error() && first.is_string();
}
simdjson_inline bool simdjson_result<dom::element>::is_int64() const noexcept {
return !error() && first.is_int64();
}
simdjson_inline bool simdjson_result<dom::element>::is_uint64() const noexcept {
return !error() && first.is_uint64();
}
simdjson_inline bool simdjson_result<dom::element>::is_double() const noexcept {
return !error() && first.is_double();
}
simdjson_inline bool simdjson_result<dom::element>::is_number() const noexcept {
return !error() && first.is_number();
}
simdjson_inline bool simdjson_result<dom::element>::is_bool() const noexcept {
return !error() && first.is_bool();
}
simdjson_inline bool simdjson_result<dom::element>::is_null() const noexcept {
return !error() && first.is_null();
}
simdjson_inline bool simdjson_result<dom::element>::is_bigint() const noexcept {
return !error() && first.is_bigint();
}
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::operator[](std::string_view key) const noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::operator[](const char *key) const noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_pointer(const std::string_view json_pointer) const noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_path(const std::string_view json_path) const noexcept {
if (error()) { return error(); }
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<std::vector<dom::element>> simdjson_result<dom::element>::at_path_with_wildcard(const std::string_view json_path) const noexcept {
if (error()) { return error(); }
return first.at_path_with_wildcard(json_path);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at(const std::string_view json_pointer) const noexcept {
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
if (error()) { return error(); }
return first.at(json_pointer);
SIMDJSON_POP_DISABLE_WARNINGS
}
#endif // SIMDJSON_DISABLE_DEPRECATED_API
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at(size_t index) const noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_key(std::string_view key) const noexcept {
if (error()) { return error(); }
return first.at_key(key);
}
simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_key_case_insensitive(std::string_view key) const noexcept {
if (error()) { return error(); }
return first.at_key_case_insensitive(key);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline simdjson_result<dom::element>::operator bool() const noexcept(false) {
return get<bool>();
}
simdjson_inline simdjson_result<dom::element>::operator const char *() const noexcept(false) {
return get<const char *>();
}
simdjson_inline simdjson_result<dom::element>::operator std::string_view() const noexcept(false) {
return get<std::string_view>();
}
simdjson_inline simdjson_result<dom::element>::operator uint64_t() const noexcept(false) {
return get<uint64_t>();
}
simdjson_inline simdjson_result<dom::element>::operator int64_t() const noexcept(false) {
return get<int64_t>();
}
simdjson_inline simdjson_result<dom::element>::operator double() const noexcept(false) {
return get<double>();
}
simdjson_inline simdjson_result<dom::element>::operator dom::array() const noexcept(false) {
return get<dom::array>();
}
simdjson_inline simdjson_result<dom::element>::operator dom::object() const noexcept(false) {
return get<dom::object>();
}
simdjson_inline dom::array::iterator simdjson_result<dom::element>::begin() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.begin();
}
simdjson_inline dom::array::iterator simdjson_result<dom::element>::end() const noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.end();
}
#endif // SIMDJSON_EXCEPTIONS
namespace internal {
// Appends the children of e, which must be an array or an object.
inline void get_container_values(const dom::element &e, std::vector<dom::element> &out) noexcept {
dom::array a;
if (!e.get_array().get(a)) {
a.get_values(out);
return;
}
dom::object o;
if (!e.get_object().get(o)) {
o.get_values(out);
}
}
// Walks a pre-parsed wildcard path from e, which is at depth `step` of the
// path. Errors below the starting node are skipped, not reported.
inline simdjson_result<std::vector<dom::element>> at_wildcard_json_path(const dom::element &e, const wildcard_json_path &path, size_t step) noexcept {
const dom::element_type t = e.type();
if (t != dom::element_type::ARRAY && t != dom::element_type::OBJECT) {
return std::vector<dom::element>{};
}
std::vector<dom::element> children;
if (step == path.steps.size()) {
switch (path.tail) {
case wildcard_json_path::tail_type::all_values:
get_container_values(e, children);
return children;
case wildcard_json_path::tail_type::pointer: {
dom::element result;
auto error = e.at_pointer(path.tail_pointer).get(result);
if (error) {
return error;
}
return std::vector<dom::element>{std::move(result)};
}
default:
return INVALID_JSON_POINTER;
}
}
const std::string &key_pointer = path.steps[step];
if (key_pointer == "/*") {
get_container_values(e, children);
} else {
dom::element child;
if (!e.at_pointer(key_pointer).get(child)) {
children.emplace_back(child);
}
}
std::vector<dom::element> result;
for (const dom::element &child : children) {
std::vector<dom::element> child_result;
if (at_wildcard_json_path(child, path, step + 1).get(child_result)) {
continue;
}
result.insert(result.end(),
std::make_move_iterator(child_result.begin()),
std::make_move_iterator(child_result.end()));
}
return result;
}
} // namespace internal
namespace dom {
//
// element inline implementation
//
simdjson_inline element::element() noexcept : tape{} {}
simdjson_inline element::element(const internal::tape_ref &_tape) noexcept : tape{_tape} { }
inline element_type element::type() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
auto tape_type = tape.tape_ref_type();
return tape_type == internal::tape_type::FALSE_VALUE ? element_type::BOOL : static_cast<element_type>(tape_type);
}
inline simdjson_result<bool> element::get_bool() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(tape.is_true()) {
return true;
} else if(tape.is_false()) {
return false;
}
return INCORRECT_TYPE;
}
inline simdjson_result<std::string_view> element::get_bigint() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable());
switch (tape.tape_ref_type()) {
case internal::tape_type::BIGINT:
return tape.get_string_view();
default:
return INCORRECT_TYPE;
}
}
inline simdjson_result<const char *> element::get_c_str() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::STRING: {
return tape.get_c_str();
}
default:
return INCORRECT_TYPE;
}
}
inline simdjson_result<size_t> element::get_string_length() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::STRING: {
return tape.get_string_length();
}
default:
return INCORRECT_TYPE;
}
}
inline simdjson_result<std::string_view> element::get_string() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::STRING:
return tape.get_string_view();
default:
return INCORRECT_TYPE;
}
}
#if SIMDJSON_SUPPORTS_CHAR8_T
inline simdjson_result<std::u8string_view> element::get_u8string() const noexcept {
std::string_view v;
SIMDJSON_TRY(get_string().get(v));
return internal::as_u8string_view(v);
}
#endif
inline simdjson_result<uint64_t> element::get_uint64() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(simdjson_unlikely(!tape.is_uint64())) { // branch rarely taken
if(tape.is_int64()) {
int64_t result = tape.next_tape_value<int64_t>();
if (result < 0) {
return NUMBER_OUT_OF_RANGE;
}
return uint64_t(result);
}
return INCORRECT_TYPE;
}
return tape.next_tape_value<int64_t>();
}
inline simdjson_result<int64_t> element::get_int64() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
if(simdjson_unlikely(!tape.is_int64())) { // branch rarely taken
if(tape.is_uint64()) {
uint64_t result = tape.next_tape_value<uint64_t>();
// Wrapping max in parens to handle Windows issue: https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std
if (result > uint64_t((std::numeric_limits<int64_t>::max)())) {
return NUMBER_OUT_OF_RANGE;
}
return static_cast<int64_t>(result);
}
return INCORRECT_TYPE;
}
return tape.next_tape_value<int64_t>();
}
inline simdjson_result<double> element::get_double() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
// Performance considerations:
// 1. Querying tape_ref_type() implies doing a shift, it is fast to just do a straight
// comparison.
// 2. Using a switch-case relies on the compiler guessing what kind of code generation
// we want... But the compiler cannot know that we expect the type to be "double"
// most of the time.
// We can expect get<double> to refer to a double type almost all the time.
// It is important to craft the code accordingly so that the compiler can use this
// information. (This could also be solved with profile-guided optimization.)
if(simdjson_unlikely(!tape.is_double())) { // branch rarely taken
if(tape.is_uint64()) {
return double(tape.next_tape_value<uint64_t>());
} else if(tape.is_int64()) {
return double(tape.next_tape_value<int64_t>());
}
return INCORRECT_TYPE;
}
// this is common:
return tape.next_tape_value<double>();
}
inline simdjson_result<array> element::get_array() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_ARRAY:
return array(tape);
default:
return INCORRECT_TYPE;
}
}
inline simdjson_result<object> element::get_object() const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_OBJECT:
return object(tape);
default:
return INCORRECT_TYPE;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code element::get(T &value) const noexcept {
return get<T>().get(value);
}
// An element-specific version prevents recursion with simdjson_result::get<element>(value)
template<>
simdjson_warn_unused simdjson_inline error_code element::get<element>(element &value) const noexcept {
value = element(tape);
return SUCCESS;
}
template<typename T>
inline void element::tie(T &value, error_code &error) && noexcept {
error = get<T>(value);
}
template<typename T>
simdjson_inline bool element::is() const noexcept {
auto result = get<T>();
return !result.error();
}
template<> inline simdjson_result<array> element::get<array>() const noexcept { return get_array(); }
template<> inline simdjson_result<object> element::get<object>() const noexcept { return get_object(); }
template<> inline simdjson_result<const char *> element::get<const char *>() const noexcept { return get_c_str(); }
template<> inline simdjson_result<std::string_view> element::get<std::string_view>() const noexcept { return get_string(); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> inline simdjson_result<std::u8string_view> element::get<std::u8string_view>() const noexcept { return get_u8string(); }
#endif
template<> inline simdjson_result<int64_t> element::get<int64_t>() const noexcept { return get_int64(); }
template<> inline simdjson_result<uint64_t> element::get<uint64_t>() const noexcept { return get_uint64(); }
template<> inline simdjson_result<double> element::get<double>() const noexcept { return get_double(); }
template<> inline simdjson_result<bool> element::get<bool>() const noexcept { return get_bool(); }
inline bool element::is_array() const noexcept { return is<array>(); }
inline bool element::is_object() const noexcept { return is<object>(); }
inline bool element::is_string() const noexcept { return is<std::string_view>(); }
inline bool element::is_int64() const noexcept { return is<int64_t>(); }
inline bool element::is_uint64() const noexcept { return is<uint64_t>(); }
inline bool element::is_double() const noexcept { return is<double>(); }
inline bool element::is_bool() const noexcept { return is<bool>(); }
inline bool element::is_number() const noexcept { return is_int64() || is_uint64() || is_double(); }
inline bool element::is_null() const noexcept {
return tape.is_null_on_tape();
}
inline bool element::is_bigint() const noexcept {
return tape.tape_ref_type() == internal::tape_type::BIGINT;
}
#if SIMDJSON_EXCEPTIONS
inline element::operator bool() const noexcept(false) { return get<bool>(); }
inline element::operator const char*() const noexcept(false) { return get<const char *>(); }
inline element::operator std::string_view() const noexcept(false) { return get<std::string_view>(); }
inline element::operator uint64_t() const noexcept(false) { return get<uint64_t>(); }
inline element::operator int64_t() const noexcept(false) { return get<int64_t>(); }
inline element::operator double() const noexcept(false) { return get<double>(); }
inline element::operator array() const noexcept(false) { return get<array>(); }
inline element::operator object() const noexcept(false) { return get<object>(); }
inline array::iterator element::begin() const noexcept(false) {
return get<array>().begin();
}
inline array::iterator element::end() const noexcept(false) {
return get<array>().end();
}
#endif // SIMDJSON_EXCEPTIONS
inline simdjson_result<element> element::operator[](std::string_view key) const noexcept {
return at_key(key);
}
inline simdjson_result<element> element::operator[](const char *key) const noexcept {
return at_key(key);
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
inline simdjson_result<element> element::at_pointer(std::string_view json_pointer) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_OBJECT:
return object(tape).at_pointer(json_pointer);
case internal::tape_type::START_ARRAY:
return array(tape).at_pointer(json_pointer);
default: {
if (!json_pointer.empty()) { // a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
// an empty string means that we return the current node
dom::element copy(*this);
return simdjson_result<element>(std::move(copy));
}
}
}
inline simdjson_result<std::vector<element>> element::at_path_with_wildcard(std::string_view json_path) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
switch (tape.tape_ref_type()) {
case internal::tape_type::START_OBJECT:
case internal::tape_type::START_ARRAY:
return internal::at_wildcard_json_path(*this, internal::parse_wildcard_json_path(json_path), 0);
default:
return std::vector<element>{};
}
}
inline simdjson_result<element> element::at_path(std::string_view json_path) const noexcept {
auto json_pointer = json_path_to_pointer_conversion(json_path);
if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
return at_pointer(json_pointer);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]]
inline simdjson_result<element> element::at(std::string_view json_pointer) const noexcept {
// version 0.4 of simdjson allowed non-compliant pointers
auto std_pointer = (json_pointer.empty() ? "" : "/") + std::string(json_pointer.begin(), json_pointer.end());
return at_pointer(std_pointer);
}
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<element> element::at(size_t index) const noexcept {
return get<array>().at(index);
}
inline simdjson_result<element> element::at_key(std::string_view key) const noexcept {
return get<object>().at_key(key);
}
inline simdjson_result<element> element::at_key_case_insensitive(std::string_view key) const noexcept {
return get<object>().at_key_case_insensitive(key);
}
inline bool element::operator<(const element &other) const noexcept {
return tape.json_index < other.tape.json_index;
}
inline bool element::operator==(const element &other) const noexcept {
return tape.json_index == other.tape.json_index;
}
inline bool element::dump_raw_tape(std::ostream &out) const noexcept {
SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914
return tape.doc->dump_raw_tape(out);
}
inline std::ostream& operator<<(std::ostream& out, element_type type) {
switch (type) {
case element_type::ARRAY:
return out << "array";
case element_type::OBJECT:
return out << "object";
case element_type::INT64:
return out << "int64_t";
case element_type::UINT64:
return out << "uint64_t";
case element_type::DOUBLE:
return out << "double";
case element_type::STRING:
return out << "string";
case element_type::BOOL:
return out << "bool";
case element_type::NULL_VALUE:
return out << "null";
case element_type::BIGINT:
return out << "bigint";
default:
return out << "unexpected content!!!"; // abort() usage is forbidden in the library
}
}
} // namespace dom
} // namespace simdjson
#endif // SIMDJSON_ELEMENT_INL_H
/* end file simdjson/dom/element-inl.h */
#if SIMDJSON_SUPPORTS_RANGES
static_assert(std::ranges::view<simdjson::dom::array>);
static_assert(std::ranges::sized_range<simdjson::dom::array>);
#if SIMDJSON_EXCEPTIONS
static_assert(std::ranges::view<simdjson::simdjson_result<simdjson::dom::array>>);
static_assert(std::ranges::sized_range<simdjson::simdjson_result<simdjson::dom::array>>);
#endif // SIMDJSON_EXCEPTIONS
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_ARRAY_INL_H
/* end file simdjson/dom/array-inl.h */
/* including simdjson/dom/document_stream-inl.h: #include "simdjson/dom/document_stream-inl.h" */
/* begin file simdjson/dom/document_stream-inl.h */
#ifndef SIMDJSON_DOCUMENT_STREAM_INL_H
#define SIMDJSON_DOCUMENT_STREAM_INL_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/document_stream.h" */
/* skipped duplicate #include "simdjson/dom/element-inl.h" */
/* including simdjson/dom/parser-inl.h: #include "simdjson/dom/parser-inl.h" */
/* begin file simdjson/dom/parser-inl.h */
#ifndef SIMDJSON_PARSER_INL_H
#define SIMDJSON_PARSER_INL_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/document_stream.h" */
/* skipped duplicate #include "simdjson/implementation.h" */
/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */
/* skipped duplicate #include "simdjson/error-inl.h" */
/* skipped duplicate #include "simdjson/padded_string-inl.h" */
/* skipped duplicate #include "simdjson/dom/document_stream-inl.h" */
/* skipped duplicate #include "simdjson/dom/element-inl.h" */
#include <climits>
#include <cstring> /* memcmp */
namespace simdjson {
namespace dom {
//
// parser inline implementation
//
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity},
loaded_bytes(nullptr) {
}
simdjson_inline parser::parser(parser &&other) noexcept = default;
simdjson_inline parser &parser::operator=(parser &&other) noexcept = default;
inline bool parser::is_valid() const noexcept { return valid; }
inline int parser::get_error_code() const noexcept { return error; }
inline std::string parser::get_error_message() const noexcept { return error_message(error); }
inline bool parser::dump_raw_tape(std::ostream &os) const noexcept {
return valid ? doc.dump_raw_tape(os) : false;
}
inline simdjson_result<size_t> parser::read_file(std::string_view path) noexcept {
const std::string path_copy(path);
// Open the file
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
std::FILE *fp = std::fopen(path_copy.c_str(), "rb");
SIMDJSON_POP_DISABLE_WARNINGS
if (fp == nullptr) {
return IO_ERROR;
}
// Get the file size
int ret;
#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
ret = _fseeki64(fp, 0, SEEK_END);
#else
ret = std::fseek(fp, 0, SEEK_END);
#endif // _WIN64
if(ret < 0) {
std::fclose(fp);
return IO_ERROR;
}
#if SIMDJSON_VISUAL_STUDIO && !SIMDJSON_IS_32BITS
__int64 len = _ftelli64(fp);
if(len == -1L) {
std::fclose(fp);
return IO_ERROR;
}
#else
long len = std::ftell(fp);
if((len < 0) || (len == LONG_MAX)) {
std::fclose(fp);
return IO_ERROR;
}
#endif
// Make sure we have enough capacity to load the file
if (_loaded_bytes_capacity < size_t(len)) {
loaded_bytes.reset( internal::allocate_padded_buffer(len) );
if (!loaded_bytes) {
std::fclose(fp);
return MEMALLOC;
}
_loaded_bytes_capacity = len;
}
// Read the string
std::rewind(fp);
size_t bytes_read = std::fread(loaded_bytes.get(), 1, len, fp);
if (std::fclose(fp) != 0 || bytes_read != size_t(len)) {
return IO_ERROR;
}
return bytes_read;
}
inline simdjson_result<element> parser::load(std::string_view path) & noexcept {
return load_into_document(doc, path);
}
inline simdjson_result<element> parser::load_into_document(document& provided_doc, std::string_view path) & noexcept {
size_t len;
auto _error = read_file(path).get(len);
if (_error) { return _error; }
return parse_into_document(provided_doc, loaded_bytes.get(), len, false);
}
inline simdjson_result<document_stream> parser::load_many(std::string_view path, size_t batch_size) noexcept {
size_t len;
auto _error = read_file(path).get(len);
if (_error) { return _error; }
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
return document_stream(*this, reinterpret_cast<const uint8_t*>(loaded_bytes.get()), len, batch_size);
}
inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept {
// Important: we need to ensure that document has enough capacity.
// Important: It is possible that provided_doc is actually the internal 'doc' within the parser!!!
error_code _error = ensure_capacity(provided_doc, len);
if (_error) { return _error; }
if (realloc_if_needed) {
// Make sure we have enough capacity to copy len bytes
if (!loaded_bytes || _loaded_bytes_capacity < len) {
loaded_bytes.reset( internal::allocate_padded_buffer(len) );
if (!loaded_bytes) {
return MEMALLOC;
}
_loaded_bytes_capacity = len;
}
std::memcpy(static_cast<void *>(loaded_bytes.get()), buf, len);
buf = reinterpret_cast<const uint8_t*>(loaded_bytes.get());
}
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
implementation->_number_as_string = _number_as_string;
_error = implementation->parse(buf, len, provided_doc);
if (_error) { return _error; }
return provided_doc.root();
}
inline simdjson_result<element> parser::parse_into_document_unpadded(document& provided_doc, const uint8_t *buf, size_t len) & noexcept {
// Like parse_into_document with realloc_if_needed=false (no copy, parse in
// place), but we tell the implementation the buffer is not padded so stage 2
// avoids reading past buf+len: string unescaping is bounded, near-the-end
// numbers are parsed from a padded copy, and atoms use length-aware
// validators (see tape_builder). Stage 1 is already safe for unpadded input.
error_code _error = ensure_capacity(provided_doc, len);
if (_error) { return _error; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
implementation->_number_as_string = _number_as_string;
implementation->_unpadded = true;
_error = implementation->parse(buf, len, provided_doc);
implementation->_unpadded = false; // restore so later padded parses use the fast path
if (_error) { return _error; }
return provided_doc.root();
}
simdjson_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const char *buf, size_t len, bool realloc_if_needed) & noexcept {
return parse_into_document(provided_doc, reinterpret_cast<const uint8_t *>(buf), len, realloc_if_needed);
}
simdjson_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const std::string &s) & noexcept {
return parse_into_document(provided_doc, s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
}
simdjson_inline simdjson_result<element> parser::parse_into_document(document& provided_doc, const padded_string &s) & noexcept {
return parse_into_document(provided_doc, s.data(), s.length(), false);
}
inline simdjson_result<element> parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept {
return parse_into_document(doc, buf, len, realloc_if_needed);
}
simdjson_inline simdjson_result<element> parser::parse(const char *buf, size_t len, bool realloc_if_needed) & noexcept {
return parse(reinterpret_cast<const uint8_t *>(buf), len, realloc_if_needed);
}
simdjson_inline simdjson_result<element> parser::parse(const std::string &s) & noexcept {
return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING);
}
simdjson_inline simdjson_result<element> parser::parse(const padded_string &s) & noexcept {
return parse(s.data(), s.length(), false);
}
simdjson_inline simdjson_result<element> parser::parse(const padded_string_view &v) & noexcept {
return parse(v.data(), v.length(), false);
}
inline simdjson_result<element> parser::parse_unpadded(const uint8_t *buf, size_t len) & noexcept {
return parse_into_document_unpadded(doc, buf, len);
}
simdjson_inline simdjson_result<element> parser::parse_unpadded(const char *buf, size_t len) & noexcept {
return parse_unpadded(reinterpret_cast<const uint8_t *>(buf), len);
}
simdjson_inline simdjson_result<element> parser::parse_unpadded(std::string_view s) & noexcept {
return parse_unpadded(reinterpret_cast<const uint8_t *>(s.data()), s.size());
}
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
return parse_many(buf, len, batch_size, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept {
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size) noexcept {
return parse_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size) noexcept {
return parse_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const padded_string_view &v, size_t batch_size) noexcept {
return parse_many(v.data(), v.length(), batch_size);
}
inline simdjson_result<document_stream> parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, format);
}
inline simdjson_result<document_stream> parser::parse_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return parse_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::parse_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return parse_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::parse_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return parse_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::parse_many(const padded_string_view &v, size_t batch_size, stream_format format) noexcept {
return parse_many(v.data(), v.length(), batch_size, format);
}
simdjson_inline size_t parser::capacity() const noexcept {
return implementation ? implementation->capacity() : 0;
}
simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return implementation ? implementation->max_depth() : DEFAULT_MAX_DEPTH;
}
simdjson_warn_unused
inline error_code parser::allocate(size_t capacity, size_t max_depth) noexcept {
//
// Reallocate implementation if needed
//
error_code err;
if (implementation) {
err = implementation->allocate(capacity, max_depth);
} else {
err = simdjson::get_active_implementation()->create_dom_parser_implementation(capacity, max_depth, implementation);
}
if (err) { return err; }
return SUCCESS;
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
simdjson_warn_unused
inline bool parser::allocate_capacity(size_t capacity, size_t max_depth) noexcept {
return !allocate(capacity, max_depth);
}
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline error_code parser::ensure_capacity(size_t desired_capacity) noexcept {
return ensure_capacity(doc, desired_capacity);
}
inline error_code parser::ensure_capacity(document& target_document, size_t desired_capacity) noexcept {
// 1. It is wasteful to allocate a document and a parser for documents spanning less than MINIMAL_DOCUMENT_CAPACITY bytes.
// 2. If we allow desired_capacity = 0 then it is possible to exit this function with implementation == nullptr.
if(desired_capacity < MINIMAL_DOCUMENT_CAPACITY) { desired_capacity = MINIMAL_DOCUMENT_CAPACITY; }
// If we don't have enough capacity, (try to) automatically bump it.
// If the document needs allocation, do it too.
// Both in one if statement to minimize unlikely branching.
//
// Note: we must make sure that this function is called if capacity() == 0. We do so because we
// ensure that desired_capacity > 0.
if (simdjson_unlikely(capacity() < desired_capacity || target_document.capacity() < desired_capacity)) {
if (desired_capacity > max_capacity()) {
return error = CAPACITY;
}
error_code err1 = target_document.capacity() < desired_capacity ? target_document.allocate(desired_capacity) : SUCCESS;
error_code err2 = capacity() < desired_capacity ? allocate(desired_capacity, max_depth()) : SUCCESS;
if(err1 != SUCCESS) { return error = err1; }
if(err2 != SUCCESS) { return error = err2; }
}
return SUCCESS;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = MINIMAL_DOCUMENT_CAPACITY;
}
}
} // namespace dom
} // namespace simdjson
#endif // SIMDJSON_PARSER_INL_H
/* end file simdjson/dom/parser-inl.h */
/* skipped duplicate #include "simdjson/error-inl.h" */
/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */
namespace simdjson {
namespace dom {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, dom::parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif
simdjson_inline document_stream::document_stream(
dom::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
#ifdef SIMDJSON_THREADS_ENABLED
if(worker.get() == nullptr) {
error = MEMALLOC;
}
#endif
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept {
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline document_stream::iterator::reference document_stream::iterator::operator*() noexcept {
// Note that in case of error, we do not yet mark
// the iterator as "finished": this detection is done
// in the operator++ function since it is possible
// to call operator++ repeatedly while omitting
// calls to operator*.
if (stream->error) { return stream->error; }
return stream->parser->doc.root();
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->ensure_capacity(batch_size);
if (error) { return; }
parser->implementation->_number_as_string = parser->number_as_string();
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread if needed
error = stage1_thread_parser.ensure_capacity(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
next();
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
const char* start = reinterpret_cast<const char*>(stream->buf) + current_index();
if (stream->error) {
return std::string_view(start, stream->len - current_index());
}
bool object_or_array = ((*start == '[') || (*start == '{'));
if(object_or_array) {
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index - 1];
return std::string_view(start, next_doc_index - current_index() + 1);
} else {
size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index];
size_t svlen = next_doc_index - current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_doc_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464 json_sequence
// mode the scanner classifies RS as a scalar character, so an RS-prefixed
// scalar document (number/true/false/null/string) has no closing structural
// index and the slice runs all the way up to the next document's RS. RS
// cannot legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping it is
// safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
inline void document_stream::next() noexcept {
// We always exit at once, once in an error condition.
if (error) { return; }
// Load the next document from the batch
doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index];
error = parser->implementation->stage2_next(parser->doc);
// If that was the last document in the batch, load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
// Run stage 2 on the first document in the batch
doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index];
error = parser->implementation->stage2_next(parser->doc);
}
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept {
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(*parser, stage1_thread_parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace dom
simdjson_inline simdjson_result<dom::document_stream>::simdjson_result() noexcept
: simdjson_result_base() {
}
simdjson_inline simdjson_result<dom::document_stream>::simdjson_result(error_code error) noexcept
: simdjson_result_base(error) {
}
simdjson_inline simdjson_result<dom::document_stream>::simdjson_result(dom::document_stream &&value) noexcept
: simdjson_result_base(std::forward<dom::document_stream>(value)) {
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline dom::document_stream::iterator simdjson_result<dom::document_stream>::begin() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.begin();
}
simdjson_inline dom::document_stream::iterator simdjson_result<dom::document_stream>::end() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.end();
}
#else // SIMDJSON_EXCEPTIONS
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
simdjson_inline dom::document_stream::iterator simdjson_result<dom::document_stream>::begin() noexcept {
first.error = error();
return first.begin();
}
simdjson_inline dom::document_stream::iterator simdjson_result<dom::document_stream>::end() noexcept {
first.error = error();
return first.end();
}
#endif // SIMDJSON_DISABLE_DEPRECATED_API
#endif // SIMDJSON_EXCEPTIONS
} // namespace simdjson
#endif // SIMDJSON_DOCUMENT_STREAM_INL_H
/* end file simdjson/dom/document_stream-inl.h */
/* including simdjson/dom/document-inl.h: #include "simdjson/dom/document-inl.h" */
/* begin file simdjson/dom/document-inl.h */
#ifndef SIMDJSON_DOCUMENT_INL_H
#define SIMDJSON_DOCUMENT_INL_H
// Inline implementations go in here.
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/document.h" */
/* skipped duplicate #include "simdjson/dom/element-inl.h" */
/* skipped duplicate #include "simdjson/internal/tape_ref-inl.h" */
/* including simdjson/internal/jsonformatutils.h: #include "simdjson/internal/jsonformatutils.h" */
/* begin file simdjson/internal/jsonformatutils.h */
#ifndef SIMDJSON_INTERNAL_JSONFORMATUTILS_H
#define SIMDJSON_INTERNAL_JSONFORMATUTILS_H
/* skipped duplicate #include "simdjson/base.h" */
#include <iomanip>
#include <ostream>
#include <sstream>
namespace simdjson {
namespace internal {
inline std::ostream& operator<<(std::ostream& out, const escape_json_string &str);
class escape_json_string {
public:
escape_json_string(std::string_view _str) noexcept : str{_str} {}
operator std::string() const noexcept { std::stringstream s; s << *this; return s.str(); }
private:
std::string_view str;
friend std::ostream& operator<<(std::ostream& out, const escape_json_string &unescaped);
};
inline std::ostream& operator<<(std::ostream& out, const escape_json_string &unescaped) {
for (size_t i=0; i<unescaped.str.length(); i++) {
switch (unescaped.str[i]) {
case '\b':
out << "\\b";
break;
case '\f':
out << "\\f";
break;
case '\n':
out << "\\n";
break;
case '\r':
out << "\\r";
break;
case '\"':
out << "\\\"";
break;
case '\t':
out << "\\t";
break;
case '\\':
out << "\\\\";
break;
default:
if (static_cast<unsigned char>(unescaped.str[i]) <= 0x1F) {
// TODO can this be done once at the beginning, or will it mess up << char?
std::ios::fmtflags f(out.flags());
out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(unescaped.str[i]);
out.flags(f);
} else {
out << unescaped.str[i];
}
}
}
return out;
}
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_JSONFORMATUTILS_H
/* end file simdjson/internal/jsonformatutils.h */
#include <cstring>
namespace simdjson {
namespace dom {
//
// document inline implementation
//
inline element document::root() const noexcept {
return element(internal::tape_ref(this, 1));
}
simdjson_warn_unused
inline size_t document::capacity() const noexcept {
return allocated_capacity;
}
simdjson_warn_unused
inline error_code document::allocate(size_t capacity) noexcept {
if (capacity == 0) {
string_buf.reset();
tape.reset();
allocated_capacity = 0;
return SUCCESS;
}
if (capacity > SIMDJSON_MAXSIZE_BYTES) {
return CAPACITY;
}
// a pathological input like "[[[[..." would generate capacity tape elements, so
// need a capacity of at least capacity + 1, but it is also possible to do
// worse with "[7,7,7,7,6,7,7,7,6,7,7,6,[7,7,7,7,6,7,7,7,6,7,7,6,7,7,7,7,7,7,6"
//where capacity + 1 tape elements are
// generated, see issue https://github.com/simdjson/simdjson/issues/345
if(capacity + 3 < capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t tape_capacity = SIMDJSON_ROUNDUP_N(capacity + 3, 64);
// a document with only zero-length strings... could have capacity/3 string
// and we would need capacity/3 * 5 bytes on the string buffer
if(5 * (capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset( new (std::nothrow) uint8_t[string_capacity]);
tape.reset(new (std::nothrow) uint64_t[tape_capacity]);
if(!(string_buf && tape)) {
allocated_capacity = 0;
string_buf.reset();
tape.reset();
return MEMALLOC;
}
// Technically the allocated_capacity might be larger than capacity
// so the next line is pessimistic.
allocated_capacity = capacity;
return SUCCESS;
}
inline bool document::dump_raw_tape(std::ostream &os) const noexcept {
uint32_t string_length;
size_t tape_idx = 0;
uint64_t tape_val = tape[tape_idx];
uint8_t type = uint8_t(tape_val >> 56);
os << tape_idx << " : " << type;
tape_idx++;
size_t how_many = 0;
if (type == 'r') {
how_many = size_t(tape_val & internal::JSON_VALUE_MASK);
} else {
// Error: no starting root node?
return false;
}
os << "\t// pointing to " << how_many << " (right after last node)\n";
uint64_t payload;
for (; tape_idx < how_many; tape_idx++) {
os << tape_idx << " : ";
tape_val = tape[tape_idx];
payload = tape_val & internal::JSON_VALUE_MASK;
type = uint8_t(tape_val >> 56);
switch (type) {
case '"': // we have a string
os << "string \"";
std::memcpy(&string_length, string_buf.get() + payload, sizeof(uint32_t));
os << internal::escape_json_string(std::string_view(
reinterpret_cast<const char *>(string_buf.get() + payload + sizeof(uint32_t)),
string_length
));
os << '"';
os << '\n';
break;
case 'l': // we have a long int
if (tape_idx + 1 >= how_many) {
return false;
}
os << "integer " << static_cast<int64_t>(tape[++tape_idx]) << "\n";
break;
case 'u': // we have a long uint
if (tape_idx + 1 >= how_many) {
return false;
}
os << "unsigned integer " << tape[++tape_idx] << "\n";
break;
case 'd': // we have a double
os << "float ";
if (tape_idx + 1 >= how_many) {
return false;
}
double answer;
std::memcpy(&answer, &tape[++tape_idx], sizeof(answer));
os << answer << '\n';
break;
case 'n': // we have a null
os << "null\n";
break;
case 't': // we have a true
os << "true\n";
break;
case 'f': // we have a false
os << "false\n";
break;
case '{': // we have an object
os << "{\t// pointing to next tape location " << uint32_t(payload)
<< " (first node after the scope), "
<< " saturated count "
<< ((payload >> 32) & internal::JSON_COUNT_MASK)<< "\n";
break; case '}': // we end an object
os << "}\t// pointing to previous tape location " << uint32_t(payload)
<< " (start of the scope)\n";
break;
case '[': // we start an array
os << "[\t// pointing to next tape location " << uint32_t(payload)
<< " (first node after the scope), "
<< " saturated count "
<< ((payload >> 32) & internal::JSON_COUNT_MASK)<< "\n";
break;
case ']': // we end an array
os << "]\t// pointing to previous tape location " << uint32_t(payload)
<< " (start of the scope)\n";
break;
case 'r': // we start and end with the root node
// should we be hitting the root node?
return false;
case 'Z': // we have a big integer
os << "bigint ";
std::memcpy(&string_length, string_buf.get() + payload, sizeof(uint32_t));
os << std::string_view(
reinterpret_cast<const char *>(string_buf.get() + payload + sizeof(uint32_t)),
string_length
);
os << '\n';
break;
default:
return false;
}
}
tape_val = tape[tape_idx];
payload = tape_val & internal::JSON_VALUE_MASK;
type = uint8_t(tape_val >> 56);
os << tape_idx << " : " << type << "\t// pointing to " << payload
<< " (start root)\n";
return true;
}
} // namespace dom
} // namespace simdjson
#endif // SIMDJSON_DOCUMENT_INL_H
/* end file simdjson/dom/document-inl.h */
/* skipped duplicate #include "simdjson/dom/element-inl.h" */
/* skipped duplicate #include "simdjson/dom/object-inl.h" */
/* skipped duplicate #include "simdjson/dom/parser-inl.h" */
/* skipped duplicate #include "simdjson/internal/tape_ref-inl.h" */
/* including simdjson/dom/serialization-inl.h: #include "simdjson/dom/serialization-inl.h" */
/* begin file simdjson/dom/serialization-inl.h */
#ifndef SIMDJSON_SERIALIZATION_INL_H
#define SIMDJSON_SERIALIZATION_INL_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/parser.h" */
/* skipped duplicate #include "simdjson/dom/serialization.h" */
/* skipped duplicate #include "simdjson/internal/tape_type.h" */
/* skipped duplicate #include "simdjson/dom/array-inl.h" */
/* skipped duplicate #include "simdjson/dom/object-inl.h" */
/* skipped duplicate #include "simdjson/internal/tape_ref-inl.h" */
#include <cmath>
#include <cstring>
namespace simdjson {
namespace dom {
inline bool parser::print_json(std::ostream &os) const noexcept {
if (!valid) {
return false;
}
simdjson::internal::string_builder<> sb;
sb.append(doc.root());
std::string_view answer = sb.str();
os << answer;
return true;
}
inline std::ostream &operator<<(std::ostream &out,
simdjson::dom::element value) {
simdjson::internal::string_builder<> sb;
sb.append(value);
return (out << sb.str());
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream &
operator<<(std::ostream &out,
simdjson::simdjson_result<simdjson::dom::element> x) {
if (x.error()) {
throw simdjson::simdjson_error(x.error());
}
return (out << x.value());
}
#endif
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value) {
simdjson::internal::string_builder<> sb;
sb.append(value);
return (out << sb.str());
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream &
operator<<(std::ostream &out,
simdjson::simdjson_result<simdjson::dom::array> x) {
if (x.error()) {
throw simdjson::simdjson_error(x.error());
}
return (out << x.value());
}
#endif
inline std::ostream &operator<<(std::ostream &out,
simdjson::dom::object value) {
simdjson::internal::string_builder<> sb;
sb.append(value);
return (out << sb.str());
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream &
operator<<(std::ostream &out,
simdjson::simdjson_result<simdjson::dom::object> x) {
if (x.error()) {
throw simdjson::simdjson_error(x.error());
}
return (out << x.value());
}
#endif
} // namespace dom
/***
* Number utility functions
**/
namespace {
/**@private
* Escape sequence like \b or \u0001
* We expect that most compilers will use 8 bytes for this data structure.
**/
struct escape_sequence {
uint8_t length;
const char
string[7]; // technically, we only ever need 6 characters, we pad to 8
};
/**@private
* This converts a signed integer into a character sequence.
* The caller is responsible for providing enough memory (at least
* 20 characters.)
* Though various runtime libraries provide itoa functions,
* it is not part of the C++ standard. The C++17 standard
* adds the to_chars functions which would do as well, but
* we want to support C++11.
*/
static char *fast_itoa(char *output, int64_t value) noexcept {
// This is a standard implementation of itoa.
char buffer[20];
uint64_t value_positive;
// In general, negating a signed integer is unsafe.
if (value < 0) {
*output++ = '-';
// Doing value_positive = -value; while avoiding
// undefined behavior warnings.
// It assumes two complement's which is universal at this
// point in time.
std::memcpy(&value_positive, &value, sizeof(value));
value_positive = (~value_positive) + 1; // this is a negation
} else {
value_positive = value;
}
// We work solely with value_positive. It *might* be easier
// for an optimizing compiler to deal with an unsigned variable
// as far as performance goes.
const char *const end_buffer = buffer + 20;
char *write_pointer = buffer + 19;
// A faster approach is possible if we expect large integers:
// unroll the loop (work in 100s, 1000s) and use some kind of
// memoization.
while (value_positive >= 10) {
*write_pointer-- = char('0' + (value_positive % 10));
value_positive /= 10;
}
*write_pointer = char('0' + value_positive);
size_t len = end_buffer - write_pointer;
std::memcpy(output, write_pointer, len);
return output + len;
}
/**@private
* This converts an unsigned integer into a character sequence.
* The caller is responsible for providing enough memory (at least
* 19 characters.)
* Though various runtime libraries provide itoa functions,
* it is not part of the C++ standard. The C++17 standard
* adds the to_chars functions which would do as well, but
* we want to support C++11.
*/
static char *fast_itoa(char *output, uint64_t value) noexcept {
// This is a standard implementation of itoa.
char buffer[20];
const char *const end_buffer = buffer + 20;
char *write_pointer = buffer + 19;
// A faster approach is possible if we expect large integers:
// unroll the loop (work in 100s, 1000s) and use some kind of
// memoization.
while (value >= 10) {
*write_pointer-- = char('0' + (value % 10));
value /= 10;
};
*write_pointer = char('0' + value);
size_t len = end_buffer - write_pointer;
std::memcpy(output, write_pointer, len);
return output + len;
}
} // anonymous namespace
namespace internal {
/***
* Minifier/formatter code.
**/
template <class formatter>
simdjson_inline void base_formatter<formatter>::number(uint64_t x) {
char number_buffer[24];
char *newp = fast_itoa(number_buffer, x);
chars(number_buffer, newp);
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::number(int64_t x) {
char number_buffer[24];
char *newp = fast_itoa(number_buffer, x);
chars(number_buffer, newp);
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::number(double x) {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(x))) {
if (std::isnan(x)) {
char const *s = "NaN";
chars(s, s + 3);
} else {
if (x < 0) {
one_char('-');
}
char const *s = "Infinity";
chars(s, s + 8);
}
return;
}
#endif
// Must be to_chars_buffer_size (40): only ~24 chars are emitted, but
// to_chars over-writes with fixed-size 16/17-byte copies for inlining.
char number_buffer[simdjson::internal::to_chars_buffer_size];
// Currently, passing the nullptr to the second argument is
// safe because our implementation does not check the second
// argument.
char *newp = simdjson::internal::to_chars(number_buffer, nullptr, x);
chars(number_buffer, newp);
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::start_array() {
one_char('[');
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::end_array() {
one_char(']');
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::start_object() {
one_char('{');
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::end_object() {
one_char('}');
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::comma() {
one_char(',');
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::true_atom() {
const char *s = "true";
chars(s, s + 4);
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::false_atom() {
const char *s = "false";
chars(s, s + 5);
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::null_atom() {
const char *s = "null";
chars(s, s + 4);
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::one_char(char c) {
buffer.push_back(c);
}
template <class formatter>
simdjson_inline void base_formatter<formatter>::chars(const char *begin,
const char *end) {
buffer.append(begin, end);
}
template <class formatter>
simdjson_inline void
base_formatter<formatter>::key(std::string_view unescaped) {
string(unescaped);
one_char(':');
}
template <class formatter>
simdjson_inline void
base_formatter<formatter>::string(std::string_view unescaped) {
one_char('\"');
size_t i = 0;
// Fast path for the case where we have no control character, no ", and no
// backslash. This should include most keys.
//
// We would like to use 'bool' but some compilers take offense to bitwise
// operation with bool types.
constexpr static char needs_escaping[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
for (; i + 8 <= unescaped.length(); i += 8) {
// Poor's man vectorization. This could get much faster if we used SIMD.
//
// It is not the case that replacing '|' with '||' would be neutral
// performance-wise.
if (needs_escaping[uint8_t(unescaped[i])] |
needs_escaping[uint8_t(unescaped[i + 1])] |
needs_escaping[uint8_t(unescaped[i + 2])] |
needs_escaping[uint8_t(unescaped[i + 3])] |
needs_escaping[uint8_t(unescaped[i + 4])] |
needs_escaping[uint8_t(unescaped[i + 5])] |
needs_escaping[uint8_t(unescaped[i + 6])] |
needs_escaping[uint8_t(unescaped[i + 7])]) {
break;
}
}
for (; i < unescaped.length(); i++) {
if (needs_escaping[uint8_t(unescaped[i])]) {
break;
}
}
// The following is also possible and omits a 256-byte table, but it is
// slower: for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F)
// && (unescaped[i] != '\"') && (unescaped[i] != '\\'); i++) {}
// At least for long strings, the following should be fast. We could
// do better by integrating the checks and the insertion.
chars(unescaped.data(), unescaped.data() + i);
// We caught a control character if we enter this loop (slow).
// Note that we are do not restart from the beginning, but rather we continue
// from the point where we encountered something that requires escaping.
for (; i < unescaped.length(); i++) {
switch (unescaped[i]) {
case '\"': {
const char *s = "\\\"";
chars(s, s + 2);
} break;
case '\\': {
const char *s = "\\\\";
chars(s, s + 2);
} break;
default:
if (uint8_t(unescaped[i]) <= 0x1F) {
// If packed, this uses 8 * 32 bytes.
// Note that we expect most compilers to embed this code in the data
// section.
constexpr static escape_sequence escaped[32] = {
{6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"},
{6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"},
{2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"},
{2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"},
{6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"},
{6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"},
{6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"},
{6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}};
auto u = escaped[uint8_t(unescaped[i])];
chars(u.string, u.string + u.length);
} else {
one_char(unescaped[i]);
}
} // switch
} // for
one_char('\"');
}
template <class formatter> inline void base_formatter<formatter>::clear() {
buffer.clear();
}
template <class formatter>
simdjson_inline std::string_view base_formatter<formatter>::str() const {
return buffer.str();
}
simdjson_inline void mini_formatter::print_newline() { return; }
simdjson_inline void mini_formatter::print_indents(size_t depth) {
(void)depth;
return;
}
simdjson_inline void mini_formatter::print_space() { return; }
simdjson_inline void pretty_formatter::print_newline() { one_char('\n'); }
simdjson_inline void pretty_formatter::print_indents(size_t depth) {
if (this->indent_step <= 0) {
return;
}
for (size_t i = 0; i < this->indent_step * depth; i++) {
one_char(' ');
}
}
simdjson_inline void pretty_formatter::print_space() { one_char(' '); }
/***
* String building code.
**/
template <class serializer>
inline void string_builder<serializer>::append(simdjson::dom::element value) {
// using tape_type = simdjson::internal::tape_type;
size_t depth = 0;
constexpr size_t MAX_DEPTH = 16;
bool is_object[MAX_DEPTH];
is_object[0] = false;
bool after_value = false;
internal::tape_ref iter(value.tape);
do {
// print commas after each value
if (after_value) {
format.comma();
format.print_newline();
}
format.print_indents(depth);
// If we are in an object, print the next key and :, and skip to the next
// value.
if (is_object[depth]) {
format.key(iter.get_string_view());
format.print_space();
iter.json_index++;
}
switch (iter.tape_ref_type()) {
// Arrays
case tape_type::START_ARRAY: {
// If we're too deep, we need to recurse to go deeper.
depth++;
if (simdjson_unlikely(depth >= MAX_DEPTH)) {
append(simdjson::dom::array(iter));
iter.json_index = iter.matching_brace_index() - 1; // Jump to the ]
depth--;
break;
}
// Output start [
format.start_array();
iter.json_index++;
// Handle empty [] (we don't want to come back around and print commas)
if (iter.tape_ref_type() == tape_type::END_ARRAY) {
format.end_array();
depth--;
break;
}
is_object[depth] = false;
after_value = false;
format.print_newline();
continue;
}
// Objects
case tape_type::START_OBJECT: {
// If we're too deep, we need to recurse to go deeper.
depth++;
if (simdjson_unlikely(depth >= MAX_DEPTH)) {
append(simdjson::dom::object(iter));
iter.json_index = iter.matching_brace_index() - 1; // Jump to the }
depth--;
break;
}
// Output start {
format.start_object();
iter.json_index++;
// Handle empty {} (we don't want to come back around and print commas)
if (iter.tape_ref_type() == tape_type::END_OBJECT) {
format.end_object();
depth--;
break;
}
is_object[depth] = true;
after_value = false;
format.print_newline();
continue;
}
// Scalars
case tape_type::STRING:
format.string(iter.get_string_view());
break;
case tape_type::BIGINT: {
// Big integer stored as string -- output raw digits (no quotes)
auto sv = iter.get_string_view();
format.chars(sv.data(), sv.data() + sv.size());
break;
}
case tape_type::INT64:
format.number(iter.next_tape_value<int64_t>());
iter.json_index++; // numbers take up 2 spots, so we need to increment
// extra
break;
case tape_type::UINT64:
format.number(iter.next_tape_value<uint64_t>());
iter.json_index++; // numbers take up 2 spots, so we need to increment
// extra
break;
case tape_type::DOUBLE:
format.number(iter.next_tape_value<double>());
iter.json_index++; // numbers take up 2 spots, so we need to increment
// extra
break;
case tape_type::TRUE_VALUE:
format.true_atom();
break;
case tape_type::FALSE_VALUE:
format.false_atom();
break;
case tape_type::NULL_VALUE:
format.null_atom();
break;
// These are impossible
case tape_type::END_ARRAY:
case tape_type::END_OBJECT:
case tape_type::ROOT:
SIMDJSON_UNREACHABLE();
}
iter.json_index++;
after_value = true;
// Handle multiple ends in a row
while (depth != 0 && (iter.tape_ref_type() == tape_type::END_ARRAY ||
iter.tape_ref_type() == tape_type::END_OBJECT)) {
format.print_newline();
depth--;
format.print_indents(depth);
if (iter.tape_ref_type() == tape_type::END_ARRAY) {
format.end_array();
} else {
format.end_object();
}
iter.json_index++;
}
// Stop when we're at depth 0
} while (depth != 0);
format.print_newline();
}
template <class serializer>
inline void string_builder<serializer>::append(simdjson::dom::object value) {
format.start_object();
auto pair = value.begin();
auto end = value.end();
if (pair != end) {
append(*pair);
for (++pair; pair != end; ++pair) {
format.comma();
append(*pair);
}
}
format.end_object();
}
template <class serializer>
inline void string_builder<serializer>::append(simdjson::dom::array value) {
format.start_array();
auto iter = value.begin();
auto end = value.end();
if (iter != end) {
append(*iter);
for (++iter; iter != end; ++iter) {
format.comma();
append(*iter);
}
}
format.end_array();
}
template <class serializer>
simdjson_inline void
string_builder<serializer>::append(simdjson::dom::key_value_pair kv) {
format.key(kv.key);
append(kv.value);
}
template <class serializer>
simdjson_inline void string_builder<serializer>::clear() {
format.clear();
}
template <class serializer>
simdjson_inline std::string_view string_builder<serializer>::str() const {
return format.str();
}
} // namespace internal
} // namespace simdjson
#endif
/* end file simdjson/dom/serialization-inl.h */
/* including simdjson/dom/fractured_json-inl.h: #include "simdjson/dom/fractured_json-inl.h" */
/* begin file simdjson/dom/fractured_json-inl.h */
#ifndef SIMDJSON_DOM_FRACTURED_JSON_INL_H
#define SIMDJSON_DOM_FRACTURED_JSON_INL_H
/* skipped duplicate #include "simdjson/dom/fractured_json.h" */
/* skipped duplicate #include "simdjson/dom/serialization.h" */
/* skipped duplicate #include "simdjson/dom/element-inl.h" */
/* skipped duplicate #include "simdjson/dom/array-inl.h" */
/* skipped duplicate #include "simdjson/dom/object-inl.h" */
/* skipped duplicate #include "simdjson/dom/parser-inl.h" */
/* skipped duplicate #include "simdjson/padded_string.h" */
/* including simdjson/internal/json_structure_analyzer.h: #include "simdjson/internal/json_structure_analyzer.h" */
/* begin file simdjson/internal/json_structure_analyzer.h */
#ifndef SIMDJSON_INTERNAL_JSON_STRUCTURE_ANALYZER_H
#define SIMDJSON_INTERNAL_JSON_STRUCTURE_ANALYZER_H
/* skipped duplicate #include "simdjson/dom/base.h" */
/* skipped duplicate #include "simdjson/dom/element.h" */
/* skipped duplicate #include "simdjson/dom/array.h" */
/* skipped duplicate #include "simdjson/dom/object.h" */
/* skipped duplicate #include "simdjson/dom/fractured_json.h" */
/* skipped duplicate #include "simdjson/internal/tape_type.h" */
#include <vector>
#include <string>
#include <string_view>
#include <utility>
namespace simdjson {
namespace internal {
/**
* Layout mode for fractured JSON formatting.
*/
enum class layout_mode {
single_line, // Single line: [1, 2, 3] or {"a": 1}
compact_multiline, // Multiple items per line with breaks
table, // Tabular format for arrays of similar objects
expanded // Traditional multi-line with indentation
};
/** Kind of value found in a table column across all rows that have one. */
enum class table_column_type {
unknown,
simple, // string, bool, or null
number,
array,
object,
mixed // rows disagree on kind
};
/** Column of a table-formatted array.*/
struct table_column {
/** Column name for object rows (may be the empty string). */
std::string key{};
/** True for object rows (the column has a key), false for array rows. */
bool has_key = false;
/** Rendered length of key */
size_t key_width = 0;
table_column_type type = table_column_type::unknown;
/** Widest rendered value in this column (when fully expanding all children) */
size_t width = 0;
/** Widest plain value in this column. */
size_t plain_width = 0;
/** subcolumns (only populated if every child is an array or every child is an object) */
std::vector<table_column> children{};
};
/** Whether rows from this column list use nested vs. simple bracket padding
* One shared decision, since picking it per-row would misalign width-aligned rows. */
inline bool table_row_is_nested(const std::vector<table_column>& columns) {
for (const table_column& col : columns) {
if (col.type == table_column_type::object || col.type == table_column_type::array) {
return true;
}
}
return false;
}
/**
* Metrics computed for a JSON element during structure analysis.
* These metrics drive layout decisions and contain child metrics for recursive formatting.
*/
struct element_metrics {
/** Nesting depth score (0 = scalar, 1 = flat container, etc.) */
size_t complexity = 0;
/** Estimated character length if rendered inline (minified + spaces) */
size_t estimated_inline_len = 0;
/** Number of direct children (0 for scalars) */
size_t child_count = 0;
/** Pre-computed: can this element be rendered inline? */
bool can_inline = false;
/** Is this an array where all elements have similar structure? */
bool is_uniform_array = false;
/** for uniform arrays: this array's columns for alignment */
std::vector<table_column> table_columns{};
/** Widest table row after pruning recursive columns that don't fit into line budget */
size_t table_row_width = 0;
/** Widest table row without pruning recursive columns that don't fit into line budget */
size_t table_row_width_full = 0;
/** Child metrics for arrays and objects (in order of iteration) */
std::vector<element_metrics> children{};
/** For scalar uniform arrays (table_columns empty): the rows' common type. */
table_column_type scalar_column_type = table_column_type::unknown;
};
/** children[idx], or a default-constructed element_metrics if idx is out of range */
inline const element_metrics& child_metrics_at(const std::vector<element_metrics>& children, size_t idx) {
static const element_metrics empty{};
return idx < children.size() ? children[idx] : empty;
}
/** *ptr, or a default-constructed element_metrics if ptr is null. */
inline const element_metrics& child_metrics_at(const element_metrics* ptr) {
static const element_metrics empty{};
return ptr ? *ptr : empty;
}
/**
* Analyzes JSON structure to compute metrics for formatting decisions.
*
* The analyzer performs a single pass over the DOM to compute:
* - Complexity (nesting depth)
* - Estimated inline length
* - Array uniformity for table detection
*
* Metrics are stored hierarchically with child metrics embedded in parent metrics,
* enabling efficient lookup during formatting without address-based caching.
*/
class structure_analyzer {
public:
/** Default constructor */
structure_analyzer() : current_opts_(nullptr) {}
/** Copy constructor - deleted since class has pointer member */
structure_analyzer(const structure_analyzer&) = delete;
/** Copy assignment - deleted since class has pointer member */
structure_analyzer& operator=(const structure_analyzer&) = delete;
/** Move constructor */
structure_analyzer(structure_analyzer&&) = default;
/** Move assignment */
structure_analyzer& operator=(structure_analyzer&&) = default;
/**
* Analyze a DOM element and compute metrics.
* @param elem The element to analyze
* @param opts Formatting options that affect metric computation
* @return Metrics for the root element (with child metrics embedded)
*/
element_metrics analyze(const dom::element& elem,
const fractured_json_options& opts);
/**
* Clear state.
*/
void clear();
/**
* Analyze an array element directly (for standalone array formatting).
* @param arr The array to analyze
* @param opts Formatting options
* @return Metrics for the array
*/
element_metrics analyze_array(const dom::array& arr,
const fractured_json_options& opts);
/**
* Analyze an object element directly (for standalone object formatting).
* @param obj The object to analyze
* @param opts Formatting options
* @return Metrics for the object
*/
element_metrics analyze_object(const dom::object& obj,
const fractured_json_options& opts);
/** Decide layout at the given render depth. Kept out of analysis since
* the same metrics can render inline or expanded at different depths. */
static layout_mode decide_layout(const element_metrics& metrics,
size_t depth,
const fractured_json_options& opts,
bool has_trailing_comma = false);
private:
const fractured_json_options* current_opts_ = nullptr;
/** Recursive analysis implementation */
element_metrics analyze_element(const dom::element& elem, size_t depth) const;
/** Analyze scalar values (strings, numbers, booleans, null) */
element_metrics analyze_scalar(const dom::element& elem) const;
/** Analyze an array element */
element_metrics analyze_array(const dom::array& arr, size_t depth) const;
/** Analyze an object element */
element_metrics analyze_object(const dom::object& obj, size_t depth) const;
/** Estimate inline length for a string (including quotes and escaping) */
size_t estimate_string_length(std::string_view s) const;
/** Estimate inline length for a number */
size_t estimate_number_length(double d) const;
size_t estimate_number_length(int64_t i) const;
size_t estimate_number_length(uint64_t u) const;
/**
* Check if an array contains uniformly-shaped rows suitable for table
* formatting, filling in corresponding metrics
* @param arr The array to check
* @param metrics The array's metrics, with children already filled in
* @param depth The array's depth
*/
bool check_array_uniformity(const dom::array& arr, element_metrics& metrics, size_t depth) const;
static table_column_type classify_table_value(dom::element_type type);
/** Find common type across a set of sibling values and the widest of their rendered lengths */
static void classify_and_measure(const std::vector<std::pair<dom::element, const element_metrics*>>& values,
table_column_type& common, size_t& max_width);
/** Recursively build table columns for an array */
void build_table_columns(const std::vector<std::pair<dom::element, const element_metrics*>>& values,
std::vector<table_column>& out_columns) const;
/** Rendered width of a row, assuming each column's current */
size_t compute_columns_width(const std::vector<table_column>& columns) const;
/** Height of a column's recursion (0 = leaf). */
static size_t column_height(const table_column& column);
/** Flatten deepest columns of the table */
static bool flatten_deepest_columns(std::vector<table_column>& columns);
/** Bottom-up refresh of column widths after flatten_deepest_columns. */
void recompute_column_widths(std::vector<table_column>& columns) const;
};
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_JSON_STRUCTURE_ANALYZER_H
/* end file simdjson/internal/json_structure_analyzer.h */
/* including simdjson/internal/fractured_formatter.h: #include "simdjson/internal/fractured_formatter.h" */
/* begin file simdjson/internal/fractured_formatter.h */
#ifndef SIMDJSON_INTERNAL_FRACTURED_FORMATTER_H
#define SIMDJSON_INTERNAL_FRACTURED_FORMATTER_H
/* skipped duplicate #include "simdjson/dom/serialization.h" */
/* skipped duplicate #include "simdjson/dom/fractured_json.h" */
/* skipped duplicate #include "simdjson/internal/json_structure_analyzer.h" */
namespace simdjson {
namespace internal {
/**
* Fractured JSON formatter using CRTP pattern.
*
* This formatter intelligently chooses between different layout modes
* (inline, compact multiline, table, expanded) based on pre-computed
* structure metrics.
*/
class fractured_formatter : public base_formatter<fractured_formatter> {
public:
explicit fractured_formatter(const fractured_json_options& opts = {});
/** CRTP hook: print newline (context-aware) */
simdjson_inline void print_newline();
/** CRTP hook: print indentation */
simdjson_inline void print_indents(size_t depth);
/** CRTP hook: print space (context-aware) */
simdjson_inline void print_space();
/** Set the current layout mode */
void set_layout_mode(layout_mode mode);
/** Get the current layout mode */
layout_mode get_layout_mode() const;
/** Track current line length for compact multiline decisions */
void track_line_length(size_t chars);
/** Check if we should break to a new line in compact mode */
bool should_break_line(size_t upcoming_length) const;
/** Get the options */
const fractured_json_options& options() const;
private:
fractured_json_options options_;
layout_mode current_layout_ = layout_mode::expanded;
size_t current_line_length_ = 0;
};
/** RAII helper forcing single line layout and restoring previous mode on exit */
class scoped_single_line_mode {
public:
explicit scoped_single_line_mode(fractured_formatter& format)
: format_(format), prev_(format.get_layout_mode()) {
format_.set_layout_mode(layout_mode::single_line);
}
~scoped_single_line_mode() { format_.set_layout_mode(prev_); }
scoped_single_line_mode(const scoped_single_line_mode&) = delete;
scoped_single_line_mode& operator=(const scoped_single_line_mode&) = delete;
private:
fractured_formatter& format_;
layout_mode prev_;
};
/**
* Specialized string builder for fractured JSON formatting.
*
* This builder performs two passes:
* 1. Analyze the structure to compute metrics
* 2. Format using the metrics to make layout decisions
*/
class fractured_string_builder {
public:
fractured_string_builder(const fractured_json_options& opts = {});
/** Append a DOM element with fractured formatting */
void append(const dom::element& value);
/** Append a DOM array with fractured formatting */
void append(const dom::array& value);
/** Append a DOM object with fractured formatting */
void append(const dom::object& value);
/** Clear the builder */
simdjson_inline void clear();
/** Get the formatted string */
simdjson_inline std::string_view str() const;
private:
fractured_formatter format_;
structure_analyzer analyzer_;
fractured_json_options options_;
/** Format an element using pre-computed metrics */
void format_element(const dom::element& elem, const element_metrics& metrics, size_t depth,
bool has_trailing_comma = false);
/** Format an array with the appropriate layout */
void format_array(const dom::array& arr, const element_metrics& metrics, size_t depth,
bool has_trailing_comma = false);
/** Format an array inline: [1, 2, 3] */
void format_array_inline(const dom::array& arr, const element_metrics& metrics);
/** Format an array with compact multiline: multiple items per line */
void format_array_compact_multiline(const dom::array& arr, const element_metrics& metrics, size_t depth);
/** Like format_array_compact_multiline, but rows are cross-row aligned
* and packed using a fixed per-row slot width. */
void format_array_compact_multiline_aligned(const dom::array& arr, const element_metrics& metrics, size_t depth);
/** Format an array as a table */
void format_array_as_table(const dom::array& arr, const element_metrics& metrics, size_t depth);
/** Write one object row's columns */
void format_table_object_row(const dom::object& obj, const element_metrics& row_metrics,
const std::vector<table_column>& columns, size_t depth);
/** Write one array row's columns */
void format_table_array_row(const dom::array& arr, const element_metrics& row_metrics,
const std::vector<table_column>& columns, size_t depth);
/** Dispatches to format_table_object_row/format_table_array_row based on elem's type. */
void format_table_row(const dom::element& elem, const element_metrics& row_metrics,
const std::vector<table_column>& columns, size_t depth);
/** Row for a uniform scalar array: writes elem inline, then pads to width so every row lines up. */
void format_table_scalar_row(const dom::element& elem, const element_metrics& row_metrics,
size_t width, size_t depth, table_column_type column_type);
/** Shared per-column writer: recurses if the column has children,
* otherwise writes a plain padded value or blank. */
void format_table_row_columns(const std::vector<table_column>& columns,
const std::vector<bool>& found,
const std::vector<dom::element>& values,
const std::vector<const element_metrics*>& value_metrics,
size_t depth);
/** Writes a single aligned leaf value */
void format_table_leaf_value(const dom::element& elem, const element_metrics& vm, size_t width,
table_column_type column_type, bool needs_comma,
bool add_comma_space, size_t depth);
/** Whether, for a column of the given type, the comma goes right after the value */
bool comma_goes_before_padding(table_column_type column_type) const;
/** Format an array expanded: one item per line */
void format_array_expanded(const dom::array& arr, const element_metrics& metrics, size_t depth);
/** Format an object with the appropriate layout */
void format_object(const dom::object& obj, const element_metrics& metrics, size_t depth,
bool has_trailing_comma = false);
/** Format an object inline: {"a": 1, "b": 2} */
void format_object_inline(const dom::object& obj, const element_metrics& metrics);
/** Format an object expanded: one key per line */
void format_object_expanded(const dom::object& obj, const element_metrics& metrics, size_t depth);
/** Format a scalar value */
void format_scalar(const dom::element& elem);
/** Whether to pad this container's own brackets. */
bool bracket_padding_for(const element_metrics& metrics) const;
};
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_FRACTURED_FORMATTER_H
/* end file simdjson/internal/fractured_formatter.h */
#include <cmath>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <unordered_map>
namespace simdjson {
namespace internal {
//
// Structure Analyzer Implementation
//
inline element_metrics structure_analyzer::analyze(const dom::element& elem,
const fractured_json_options& opts) {
current_opts_ = &opts;
return analyze_element(elem, 0);
}
inline void structure_analyzer::clear() {
current_opts_ = nullptr;
}
inline element_metrics structure_analyzer::analyze_array(const dom::array& arr,
const fractured_json_options& opts) {
current_opts_ = &opts;
return analyze_array(arr, 0);
}
inline element_metrics structure_analyzer::analyze_object(const dom::object& obj,
const fractured_json_options& opts) {
current_opts_ = &opts;
return analyze_object(obj, 0);
}
inline element_metrics structure_analyzer::analyze_element(const dom::element& elem, size_t depth) const {
switch (elem.type()) {
case dom::element_type::ARRAY: {
dom::array arr;
if (elem.get_array().get(arr) == SUCCESS) {
return analyze_array(arr, depth);
}
break;
}
case dom::element_type::OBJECT: {
dom::object obj;
if (elem.get_object().get(obj) == SUCCESS) {
return analyze_object(obj, depth);
}
break;
}
default:
// Handle all scalar types with a helper
return analyze_scalar(elem);
}
return element_metrics{};
}
inline element_metrics structure_analyzer::analyze_scalar(const dom::element& elem) const {
element_metrics metrics;
metrics.complexity = 0;
metrics.child_count = 0;
metrics.can_inline = true;
switch (elem.type()) {
case dom::element_type::STRING: {
std::string_view str;
if (elem.get_string().get(str) == SUCCESS) {
metrics.estimated_inline_len = estimate_string_length(str);
}
break;
}
case dom::element_type::INT64: {
int64_t val;
if (elem.get_int64().get(val) == SUCCESS) {
metrics.estimated_inline_len = estimate_number_length(val);
}
break;
}
case dom::element_type::UINT64: {
uint64_t val;
if (elem.get_uint64().get(val) == SUCCESS) {
metrics.estimated_inline_len = estimate_number_length(val);
}
break;
}
case dom::element_type::DOUBLE: {
double val;
if (elem.get_double().get(val) == SUCCESS) {
metrics.estimated_inline_len = estimate_number_length(val);
}
break;
}
case dom::element_type::BOOL: {
bool val;
if (elem.get_bool().get(val) == SUCCESS) {
metrics.estimated_inline_len = val ? 4 : 5; // "true" or "false"
}
break;
}
case dom::element_type::NULL_VALUE:
metrics.estimated_inline_len = 4; // "null"
break;
default:
break;
}
return metrics;
}
inline element_metrics structure_analyzer::analyze_array(const dom::array& arr,
size_t depth) const {
element_metrics metrics;
metrics.complexity = 1; // At least 1 for being an array
metrics.estimated_inline_len = 2; // "[]"
metrics.child_count = 0;
size_t max_child_complexity = 0;
bool first = true;
for (dom::element child : arr) {
if (!first) {
metrics.estimated_inline_len += 2; // ", "
}
first = false;
element_metrics child_metrics = analyze_element(child, depth + 1);
metrics.estimated_inline_len += child_metrics.estimated_inline_len;
max_child_complexity = (std::max)(max_child_complexity, child_metrics.complexity);
metrics.child_count++;
metrics.children.push_back(std::move(child_metrics));
}
// Complexity is 1 + max child complexity
metrics.complexity = 1 + max_child_complexity;
// Bracket padding "[ 1, 2 ]" vs "[1, 2]"
bool use_bracket_padding = (max_child_complexity >= 1)
? current_opts_->nested_bracket_padding : current_opts_->simple_bracket_padding;
if (use_bracket_padding && metrics.child_count > 0) {
metrics.estimated_inline_len += 2;
}
// Check if can inline
metrics.can_inline = metrics.complexity <= current_opts_->max_inline_complexity;
// Check for uniform array (table formatting, or aligned compact multiline).
bool wants_table_columns =
(current_opts_->enable_table_format &&
max_child_complexity <= current_opts_->max_table_row_complexity) ||
(current_opts_->enable_compact_multiline &&
max_child_complexity <= current_opts_->max_compact_array_complexity);
if (wants_table_columns) {
metrics.is_uniform_array = check_array_uniformity(arr, metrics, depth);
}
return metrics;
}
inline element_metrics structure_analyzer::analyze_object(const dom::object& obj,
size_t depth) const {
element_metrics metrics;
metrics.complexity = 1;
metrics.estimated_inline_len = 2; // "{}"
metrics.child_count = 0;
size_t max_child_complexity = 0;
bool first = true;
for (dom::key_value_pair field : obj) {
if (!first) {
metrics.estimated_inline_len += 2; // ", "
}
first = false;
// Key length: quotes + key + colon + space
metrics.estimated_inline_len += estimate_string_length(field.key) + 2;
element_metrics child_metrics = analyze_element(field.value, depth + 1);
metrics.estimated_inline_len += child_metrics.estimated_inline_len;
max_child_complexity = (std::max)(max_child_complexity, child_metrics.complexity);
metrics.child_count++;
metrics.children.push_back(std::move(child_metrics));
}
metrics.complexity = 1 + max_child_complexity;
// Bracket padding '{ "a": 1 }' vs '{"a": 1}'
bool use_bracket_padding = (max_child_complexity >= 1)
? current_opts_->nested_bracket_padding : current_opts_->simple_bracket_padding;
if (use_bracket_padding && metrics.child_count > 0) {
metrics.estimated_inline_len += 2;
}
metrics.can_inline = metrics.complexity <= current_opts_->max_inline_complexity;
return metrics;
}
inline size_t structure_analyzer::estimate_string_length(std::string_view s) const {
size_t len = 2; // quotes
for (char c : s) {
if (c == '"' || c == '\\' || static_cast<unsigned char>(c) < 32) {
len += 2; // escape sequence (at least)
} else {
len += 1;
}
}
return len;
}
inline size_t structure_analyzer::estimate_number_length(double d) const {
if (!std::isfinite(d)) {
#if SIMDJSON_ENABLE_NAN_INF
if (std::isnan(d)) {
return 3; // "NaN"
} else if (d < 0) {
return 9; // "-Infinity"
} else {
return 8; // "Infinity"
}
#else
return 4; // "null" for invalid numbers
#endif
}
// Rough estimate: up to 17 significant digits + sign + decimal point + exponent
char buf[32];
int len = snprintf(buf, sizeof(buf), "%.17g", d);
return len > 0 ? static_cast<size_t>(len) : 20;
}
inline size_t structure_analyzer::estimate_number_length(int64_t i) const {
if (i == 0) return 1;
// Handle INT64_MIN specially to avoid overflow when negating
if (i == INT64_MIN) return 20; // "-9223372036854775808" is 20 characters
size_t len = (i < 0) ? 1 : 0; // negative sign
int64_t abs_val = (i < 0) ? -i : i;
while (abs_val > 0) {
len++;
abs_val /= 10;
}
return len;
}
inline size_t structure_analyzer::estimate_number_length(uint64_t u) const {
if (u == 0) return 1;
size_t len = 0;
while (u > 0) {
len++;
u /= 10;
}
return len;
}
inline table_column_type structure_analyzer::classify_table_value(dom::element_type type) {
switch (type) {
case dom::element_type::OBJECT: return table_column_type::object;
case dom::element_type::ARRAY: return table_column_type::array;
case dom::element_type::INT64:
case dom::element_type::UINT64:
case dom::element_type::DOUBLE: return table_column_type::number;
case dom::element_type::NULL_VALUE: return table_column_type::unknown;
default: return table_column_type::simple; // string, bool
}
}
inline void structure_analyzer::classify_and_measure(
const std::vector<std::pair<dom::element, const element_metrics*>>& values,
table_column_type& common, size_t& max_width) {
common = table_column_type::unknown;
max_width = 0;
for (const auto& v : values) {
table_column_type t = classify_table_value(v.first.type());
if (t != table_column_type::unknown) {
if (common == table_column_type::unknown) common = t;
else if (t != common) common = table_column_type::mixed;
}
if (v.second) {
max_width = (std::max)(max_width, v.second->estimated_inline_len);
}
}
}
inline void structure_analyzer::build_table_columns(
const std::vector<std::pair<dom::element, const element_metrics*>>& values,
std::vector<table_column>& out_columns) const {
out_columns.clear();
if (values.empty()) {
return;
}
table_column_type common = table_column_type::unknown;
for (const auto& v : values) {
table_column_type t = classify_table_value(v.first.type());
// Null cannot be projected into object or array columns. Keep these
// values intact by falling back to ordinary value formatting.
if (t == table_column_type::unknown) return;
if (common == table_column_type::unknown) common = t;
else if (t != common) { common = table_column_type::mixed; break; }
}
if (common != table_column_type::object && common != table_column_type::array) {
return;
}
std::vector<std::vector<std::pair<dom::element, const element_metrics*>>> per_column_values;
if (common == table_column_type::object) {
std::unordered_map<std::string_view, size_t> column_index;
// Last row (1-based) that filled each column, to detect duplicate keys.
std::vector<size_t> column_last_row;
size_t row = 0;
for (const auto& v : values) {
if (v.first.type() != dom::element_type::OBJECT) continue;
dom::object obj;
if (v.first.get_object().get(obj) != SUCCESS) continue;
row++;
size_t field_idx = 0;
for (dom::key_value_pair field : obj) {
auto it = column_index.find(field.key);
size_t col_idx;
if (it == column_index.end()) {
col_idx = out_columns.size();
column_index.emplace(field.key, col_idx);
out_columns.emplace_back();
out_columns.back().key.assign(field.key.data(), field.key.size());
out_columns.back().has_key = true;
out_columns.back().key_width = estimate_string_length(field.key);
per_column_values.emplace_back();
column_last_row.push_back(0);
} else {
col_idx = it->second;
}
// A row with a duplicate key cannot be laid out as a table: each
// column holds one value per row, so the later duplicates would be
// lost. Returning no columns also disables the aligned compact
// multiline layout for this array, which needs the same columns.
if (column_last_row[col_idx] == row) {
out_columns.clear();
return;
}
column_last_row[col_idx] = row;
const element_metrics* field_metrics = (v.second && field_idx < v.second->children.size())
? &v.second->children[field_idx] : nullptr;
per_column_values[col_idx].emplace_back(field.value, field_metrics);
field_idx++;
}
}
} else { // array: columns by position
for (const auto& v : values) {
if (v.first.type() != dom::element_type::ARRAY) continue;
dom::array sub_arr;
if (v.first.get_array().get(sub_arr) != SUCCESS) continue;
size_t idx = 0;
for (dom::element item : sub_arr) {
if (out_columns.size() <= idx) {
out_columns.emplace_back();
per_column_values.emplace_back();
}
const element_metrics* item_metrics = (v.second && idx < v.second->children.size())
? &v.second->children[idx] : nullptr;
per_column_values[idx].emplace_back(item, item_metrics);
idx++;
}
}
}
for (size_t i = 0; i < out_columns.size(); i++) {
table_column_type col_type;
size_t max_width;
classify_and_measure(per_column_values[i], col_type, max_width);
out_columns[i].type = col_type;
out_columns[i].plain_width = max_width;
if (col_type == table_column_type::object || col_type == table_column_type::array) {
build_table_columns(per_column_values[i], out_columns[i].children);
}
out_columns[i].width = out_columns[i].children.empty() ? max_width : compute_columns_width(out_columns[i].children);
}
}
inline bool structure_analyzer::check_array_uniformity(const dom::array& arr,
element_metrics& metrics,
size_t depth) const {
std::vector<std::pair<dom::element, const element_metrics*>> values;
values.reserve(metrics.child_count);
size_t row_idx = 0;
for (dom::element elem : arr) {
const element_metrics* row_metrics = (row_idx < metrics.children.size()) ? &metrics.children[row_idx] : nullptr;
values.emplace_back(elem, row_metrics);
row_idx++;
}
build_table_columns(values, metrics.table_columns);
if (metrics.table_columns.empty()) {
// Not uniformly object or array. Check for uniform scalar
table_column_type common;
size_t max_width;
classify_and_measure(values, common, max_width);
if (common != table_column_type::number && common != table_column_type::simple) {
return false;
}
metrics.scalar_column_type = common;
metrics.table_row_width = max_width;
metrics.table_row_width_full = max_width;
return true;
}
metrics.table_row_width_full = compute_columns_width(metrics.table_columns);
size_t row_indent_width = (depth + 1) * current_opts_->indent_spaces;
size_t budget = (row_indent_width + 1 >= current_opts_->max_total_line_length)
? 0 : current_opts_->max_total_line_length - row_indent_width - 1;
size_t width = metrics.table_row_width_full;
while (width > budget && flatten_deepest_columns(metrics.table_columns)) {
recompute_column_widths(metrics.table_columns);
width = compute_columns_width(metrics.table_columns);
}
metrics.table_row_width = width;
return true;
}
inline size_t structure_analyzer::compute_columns_width(const std::vector<table_column>& columns) const {
size_t width = 2; // "{}" or "[]"
if (table_row_is_nested(columns) ? current_opts_->nested_bracket_padding : current_opts_->simple_bracket_padding) {
width += 2;
}
for (const table_column& col : columns) {
if (col.has_key) {
width += col.key_width;
width += current_opts_->colon_padding ? 2 : 1;
}
width += col.width;
}
if (columns.size() > 1) {
width += (columns.size() - 1) * (current_opts_->comma_padding ? 2 : 1);
}
return width;
}
inline size_t structure_analyzer::column_height(const table_column& column) {
size_t height = 0;
for (const table_column& child : column.children) {
height = (std::max)(height, column_height(child));
}
return column.children.empty() ? 0 : height + 1;
}
inline bool structure_analyzer::flatten_deepest_columns(std::vector<table_column>& columns) {
size_t max_height = 0;
for (const table_column& col : columns) {
max_height = (std::max)(max_height, column_height(col));
}
bool changed = false;
for (table_column& col : columns) {
if (column_height(col) != max_height || max_height == 0) continue;
if (max_height == 1) {
col.children.clear();
col.width = col.plain_width;
changed = true;
} else {
changed |= flatten_deepest_columns(col.children);
}
}
return changed;
}
inline void structure_analyzer::recompute_column_widths(std::vector<table_column>& columns) const {
for (table_column& col : columns) {
if (!col.children.empty()) {
recompute_column_widths(col.children);
col.width = compute_columns_width(col.children);
}
}
}
inline layout_mode structure_analyzer::decide_layout(const element_metrics& metrics,
size_t depth,
const fractured_json_options& opts,
bool has_trailing_comma) {
if (metrics.child_count == 0) {
return layout_mode::single_line;
}
long long signed_depth = static_cast<long long>(depth);
bool depth_allows_inline_or_compact = signed_depth > opts.always_expand_depth;
bool depth_allows_table = signed_depth >= opts.always_expand_depth;
// Check inline feasibility
size_t reserved_width = depth * opts.indent_spaces + (has_trailing_comma ? 1 : 0);
if (depth_allows_inline_or_compact && metrics.can_inline &&
metrics.estimated_inline_len + reserved_width <= opts.max_total_line_length) {
return layout_mode::single_line;
}
// Rows (table's or compact multiline's) render one level deeper than the
// array itself.
size_t row_indent_width = (depth + 1) * opts.indent_spaces;
// Check compact multiline
// for uniform arrays fall back to table if we would have to flatten any formatting
bool compact_multiline_enabled = opts.enable_compact_multiline &&
metrics.complexity <= opts.max_compact_array_complexity + 1 &&
metrics.child_count >= opts.min_compact_array_row_items;
if (depth_allows_inline_or_compact && compact_multiline_enabled) {
bool aligned = metrics.is_uniform_array;
size_t comma_width = opts.comma_padding ? 2 : 1;
size_t avg_item_width;
if (aligned) {
avg_item_width = metrics.table_row_width_full + comma_width;
} else {
size_t sum = 0;
for (const element_metrics& child : metrics.children) {
sum += child.estimated_inline_len;
}
avg_item_width = comma_width + sum / metrics.child_count;
}
size_t row_pack_space = (row_indent_width >= opts.max_total_line_length)
? 0 : opts.max_total_line_length - row_indent_width;
if (avg_item_width * opts.min_compact_array_row_items <= row_pack_space) {
return layout_mode::compact_multiline;
}
}
// Check Table mode
if (depth_allows_table && opts.enable_table_format &&
metrics.is_uniform_array &&
metrics.table_row_width + 1 + row_indent_width <= opts.max_total_line_length) {
return layout_mode::table;
}
return layout_mode::expanded;
}
//
// Fractured Formatter Implementation
//
inline fractured_formatter::fractured_formatter(const fractured_json_options& opts)
: options_(opts) {}
simdjson_inline void fractured_formatter::print_newline() {
if (current_layout_ == layout_mode::single_line) {
return; // No newlines in inline mode
}
one_char('\n');
current_line_length_ = 0;
}
simdjson_inline void fractured_formatter::print_indents(size_t depth) {
if (current_layout_ == layout_mode::single_line) {
return; // No indentation in inline mode
}
for (size_t i = 0; i < depth * options_.indent_spaces; i++) {
one_char(' ');
current_line_length_++;
}
}
simdjson_inline void fractured_formatter::print_space() {
one_char(' ');
current_line_length_++;
}
inline void fractured_formatter::set_layout_mode(layout_mode mode) {
current_layout_ = mode;
}
inline layout_mode fractured_formatter::get_layout_mode() const {
return current_layout_;
}
inline void fractured_formatter::track_line_length(size_t chars) {
current_line_length_ += chars;
}
inline bool fractured_formatter::should_break_line(size_t upcoming_length) const {
return (current_line_length_ + upcoming_length) > options_.max_total_line_length;
}
inline const fractured_json_options& fractured_formatter::options() const {
return options_;
}
//
// Fractured String Builder Implementation
//
inline fractured_string_builder::fractured_string_builder(const fractured_json_options& opts)
: format_(opts), analyzer_{}, options_(opts) {}
inline void fractured_string_builder::append(const dom::element& value) {
// Phase 1: Analyze structure (metrics tree is built recursively)
element_metrics root_metrics = analyzer_.analyze(value, options_);
// Phase 2: Format using metrics tree (passed through recursion)
format_element(value, root_metrics, 0);
}
inline void fractured_string_builder::append(const dom::array& value) {
// Analyze the array to get proper metrics with children
element_metrics metrics = analyzer_.analyze_array(value, options_);
format_array(value, metrics, 0);
}
inline void fractured_string_builder::append(const dom::object& value) {
// Analyze the object to get proper metrics with children
element_metrics metrics = analyzer_.analyze_object(value, options_);
format_object(value, metrics, 0);
}
simdjson_inline void fractured_string_builder::clear() {
format_.clear();
analyzer_.clear();
}
simdjson_inline std::string_view fractured_string_builder::str() const {
return format_.str();
}
inline void fractured_string_builder::format_element(const dom::element& elem,
const element_metrics& metrics,
size_t depth,
bool has_trailing_comma) {
switch (elem.type()) {
case dom::element_type::ARRAY: {
dom::array arr;
if (elem.get_array().get(arr) == SUCCESS) {
format_array(arr, metrics, depth, has_trailing_comma);
}
break;
}
case dom::element_type::OBJECT: {
dom::object obj;
if (elem.get_object().get(obj) == SUCCESS) {
format_object(obj, metrics, depth, has_trailing_comma);
}
break;
}
default:
format_scalar(elem);
break;
}
}
inline void fractured_string_builder::format_array(const dom::array& arr,
const element_metrics& metrics,
size_t depth,
bool has_trailing_comma) {
layout_mode layout = structure_analyzer::decide_layout(metrics, depth, options_, has_trailing_comma);
switch (layout) {
case layout_mode::single_line:
format_array_inline(arr, metrics);
break;
case layout_mode::compact_multiline:
format_array_compact_multiline(arr, metrics, depth);
break;
case layout_mode::table:
format_array_as_table(arr, metrics, depth);
break;
case layout_mode::expanded:
default:
format_array_expanded(arr, metrics, depth);
break;
}
}
inline void fractured_string_builder::format_array_inline(const dom::array& arr,
const element_metrics& metrics) {
scoped_single_line_mode single_line(format_);
format_.start_array();
bool first = true;
bool empty = true;
size_t child_idx = 0;
for (dom::element elem : arr) {
empty = false;
if (!first) {
format_.comma();
if (options_.comma_padding) {
format_.print_space();
}
} else if (bracket_padding_for(metrics)) {
format_.print_space();
}
first = false;
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
format_element(elem, child_metrics, 0);
child_idx++;
}
if (bracket_padding_for(metrics) && !empty) {
format_.print_space();
}
format_.end_array();
}
inline void fractured_string_builder::format_array_compact_multiline(const dom::array& arr,
const element_metrics& metrics,
size_t depth) {
if (metrics.is_uniform_array) {
format_array_compact_multiline_aligned(arr, metrics, depth);
return;
}
format_.start_array();
format_.print_newline();
format_.print_indents(depth + 1);
bool first = true;
bool prev_item_was_expanded = false;
size_t child_idx = 0;
for (dom::element elem : arr) {
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
if (!first) {
format_.comma();
format_.track_line_length(1);
// Check if we should break to new line
if (prev_item_was_expanded ||
format_.should_break_line(child_metrics.estimated_inline_len)) {
format_.print_newline();
format_.print_indents(depth + 1);
} else if (options_.comma_padding) {
format_.print_space();
}
}
first = false;
bool is_last = (child_idx + 1 == metrics.child_count);
layout_mode item_layout = structure_analyzer::decide_layout(child_metrics, depth + 1, options_, !is_last);
bool item_fits = item_layout == layout_mode::single_line;
if (item_fits) {
{
scoped_single_line_mode single_line(format_);
format_element(elem, child_metrics, depth + 1, !is_last);
}
format_.track_line_length(child_metrics.estimated_inline_len);
} else {
format_element(elem, child_metrics, depth + 1, !is_last);
}
prev_item_was_expanded = !item_fits;
child_idx++;
}
format_.print_newline();
format_.print_indents(depth);
format_.end_array();
}
inline void fractured_string_builder::format_array_compact_multiline_aligned(
const dom::array& arr, const element_metrics& metrics, size_t depth) {
const std::vector<table_column>& columns = metrics.table_columns;
format_.start_array();
format_.print_newline();
format_.print_indents(depth + 1);
size_t indent_width = (depth + 1) * options_.indent_spaces;
size_t available_line_space = (indent_width >= options_.max_total_line_length)
? 0 : options_.max_total_line_length - indent_width;
size_t comma_width = options_.comma_padding ? 2 : 1;
size_t remaining_line_space = available_line_space;
bool first = true;
size_t child_idx = 0;
for (dom::element elem : arr) {
bool needs_comma = (child_idx + 1 < metrics.child_count);
size_t space_needed = metrics.table_row_width_full + (needs_comma ? comma_width : 0);
if (!first) {
if (remaining_line_space < space_needed) {
format_.print_newline();
format_.print_indents(depth + 1);
remaining_line_space = available_line_space;
} else if (options_.comma_padding) {
format_.print_space();
}
}
first = false;
const element_metrics& row_metrics = child_metrics_at(metrics.children, child_idx);
if (columns.empty()) {
format_table_scalar_row(elem, row_metrics, metrics.table_row_width_full, depth + 1,
metrics.scalar_column_type);
} else {
format_table_row(elem, row_metrics, columns, depth + 1);
}
if (needs_comma) {
format_.comma();
}
remaining_line_space -= (std::min)(remaining_line_space, space_needed);
child_idx++;
}
format_.print_newline();
format_.print_indents(depth);
format_.end_array();
}
inline void fractured_string_builder::format_table_row_columns(
const std::vector<table_column>& columns,
const std::vector<bool>& found,
const std::vector<dom::element>& values,
const std::vector<const element_metrics*>& value_metrics,
size_t depth) {
const size_t num_columns = columns.size();
size_t last_present_idx = num_columns;
for (size_t i = 0; i < num_columns; i++) {
if (found[i]) last_present_idx = i;
}
size_t comma_width = options_.comma_padding ? 2 : 1;
for (size_t col_idx = 0; col_idx < num_columns; col_idx++) {
const table_column& column = columns[col_idx];
const bool is_last_col = (col_idx == num_columns - 1);
if (found[col_idx]) {
if (column.has_key) {
format_.key(column.key);
if (options_.colon_padding) {
format_.print_space();
}
}
bool needs_comma = !is_last_col && (col_idx < last_present_idx);
if (!column.children.empty()) {
// Recurses into this cell's own columns instead of a plain value;
// every row aligns those the same way (blank-padding missing
// ones), so the result is always exactly column.width wide
// no padding needed afterward, unlike the leaf case below.
if (column.type == table_column_type::object) {
dom::object sub_obj;
if (values[col_idx].get_object().get(sub_obj) == SUCCESS) {
const element_metrics& sub_metrics = child_metrics_at(value_metrics[col_idx]);
format_table_object_row(sub_obj, sub_metrics, column.children, depth);
}
} else {
dom::array sub_arr;
if (values[col_idx].get_array().get(sub_arr) == SUCCESS) {
const element_metrics& sub_metrics = child_metrics_at(value_metrics[col_idx]);
format_table_array_row(sub_arr, sub_metrics, column.children, depth);
}
}
// value is already padded
if (needs_comma) {
format_.comma();
if (options_.comma_padding) {
format_.print_space();
}
}
} else {
const element_metrics& vm = child_metrics_at(value_metrics[col_idx]);
format_table_leaf_value(values[col_idx], vm, column.width, column.type, needs_comma,
/*add_comma_space=*/true, depth);
}
if (!is_last_col && !needs_comma) {
// Found, but no more real values follow: blank space where a comma would go.
for (size_t i = 0; i < comma_width; i++) {
format_.one_char(' ');
}
}
} else {
size_t slot_width = column.width;
if (column.has_key) {
slot_width += column.key_width + (options_.colon_padding ? 2 : 1);
}
for (size_t i = 0; i < slot_width; i++) {
format_.one_char(' ');
}
if (!is_last_col) {
for (size_t i = 0; i < comma_width; i++) {
format_.one_char(' ');
}
}
}
}
}
inline void fractured_string_builder::format_table_object_row(
const dom::object& obj, const element_metrics& row_metrics,
const std::vector<table_column>& columns, size_t depth) {
const size_t num_columns = columns.size();
std::vector<bool> found(num_columns, false);
std::vector<dom::element> values(num_columns);
std::vector<const element_metrics*> value_metrics(num_columns, nullptr);
for (size_t col_idx = 0; col_idx < num_columns; col_idx++) {
size_t field_idx = 0;
for (dom::key_value_pair field : obj) {
if (field.key == columns[col_idx].key) {
found[col_idx] = true;
values[col_idx] = field.value;
value_metrics[col_idx] = (field_idx < row_metrics.children.size())
? &row_metrics.children[field_idx] : nullptr;
break;
}
field_idx++;
}
}
bool nested = table_row_is_nested(columns);
format_.start_object();
if (nested ? options_.nested_bracket_padding : options_.simple_bracket_padding) {
format_.print_space();
}
format_table_row_columns(columns, found, values, value_metrics, depth);
if (nested ? options_.nested_bracket_padding : options_.simple_bracket_padding) {
format_.print_space();
}
format_.end_object();
}
inline void fractured_string_builder::format_table_array_row(
const dom::array& arr, const element_metrics& row_metrics,
const std::vector<table_column>& columns, size_t depth) {
const size_t num_columns = columns.size();
std::vector<bool> found(num_columns, false);
std::vector<dom::element> values(num_columns);
std::vector<const element_metrics*> value_metrics(num_columns, nullptr);
size_t idx = 0;
for (dom::element item : arr) {
if (idx >= num_columns) break;
found[idx] = true;
values[idx] = item;
value_metrics[idx] = (idx < row_metrics.children.size()) ? &row_metrics.children[idx] : nullptr;
idx++;
}
bool nested = table_row_is_nested(columns);
format_.start_array();
if (nested ? options_.nested_bracket_padding : options_.simple_bracket_padding) {
format_.print_space();
}
format_table_row_columns(columns, found, values, value_metrics, depth);
if (nested ? options_.nested_bracket_padding : options_.simple_bracket_padding) {
format_.print_space();
}
format_.end_array();
}
inline void fractured_string_builder::format_table_row(
const dom::element& elem, const element_metrics& row_metrics,
const std::vector<table_column>& columns, size_t depth) {
if (elem.type() == dom::element_type::ARRAY) {
dom::array arr;
if (elem.get_array().get(arr) == SUCCESS) {
format_table_array_row(arr, row_metrics, columns, depth);
}
} else {
dom::object obj;
if (elem.get_object().get(obj) == SUCCESS) {
format_table_object_row(obj, row_metrics, columns, depth);
}
}
}
inline bool fractured_string_builder::comma_goes_before_padding(table_column_type column_type) const {
switch (options_.comma_placement) {
case table_comma_placement::before_padding: return true;
case table_comma_placement::after_padding: return false;
case table_comma_placement::before_padding_except_numbers:
default:
return column_type != table_column_type::number;
}
}
inline void fractured_string_builder::format_table_leaf_value(
const dom::element& elem, const element_metrics& vm, size_t width,
table_column_type column_type, bool needs_comma, bool add_comma_space, size_t depth) {
bool comma_before_pad = needs_comma && comma_goes_before_padding(column_type);
bool comma_after_pad = needs_comma && !comma_before_pad;
bool right_align = column_type == table_column_type::number &&
options_.number_alignment == number_list_alignment::right;
size_t value_len = vm.estimated_inline_len;
size_t left_pad = 0;
size_t right_pad = 0;
if (right_align) {
left_pad = (width > value_len) ? width - value_len : 0;
comma_before_pad = needs_comma;
comma_after_pad = false;
} else {
right_pad = (width > value_len) ? width - value_len : 0;
}
for (size_t i = 0; i < left_pad; i++) {
format_.one_char(' ');
}
{
scoped_single_line_mode single_line(format_);
format_element(elem, vm, depth);
}
if (comma_before_pad) {
format_.comma();
}
for (size_t i = 0; i < right_pad; i++) {
format_.one_char(' ');
}
if (comma_after_pad) {
format_.comma();
}
if (needs_comma && add_comma_space && options_.comma_padding) {
format_.print_space();
}
}
inline void fractured_string_builder::format_table_scalar_row(
const dom::element& elem, const element_metrics& row_metrics, size_t width, size_t depth,
table_column_type column_type) {
format_table_leaf_value(elem, row_metrics, width, column_type,
/*needs_comma=*/false, /*add_comma_space=*/false, depth);
}
inline void fractured_string_builder::format_array_as_table(const dom::array& arr,
const element_metrics& metrics,
size_t depth) {
if (!metrics.is_uniform_array) {
format_array_expanded(arr, metrics, depth);
return;
}
const std::vector<table_column>& columns = metrics.table_columns;
format_.start_array();
format_.print_newline();
bool first_row = true;
size_t child_idx = 0;
for (dom::element elem : arr) {
if (!first_row) {
format_.comma();
format_.print_newline();
}
first_row = false;
format_.print_indents(depth + 1);
const element_metrics& row_metrics = child_metrics_at(metrics.children, child_idx);
if (columns.empty()) {
format_table_scalar_row(elem, row_metrics, metrics.table_row_width, depth + 1,
metrics.scalar_column_type);
} else {
format_table_row(elem, row_metrics, columns, depth + 1);
}
child_idx++;
}
format_.print_newline();
format_.print_indents(depth);
format_.end_array();
}
inline void fractured_string_builder::format_array_expanded(const dom::array& arr,
const element_metrics& metrics,
size_t depth) {
format_.start_array();
bool empty = true;
bool first = true;
size_t child_idx = 0;
for (dom::element elem : arr) {
empty = false;
if (!first) {
format_.comma();
}
first = false;
format_.print_newline();
format_.print_indents(depth + 1);
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
bool is_last = (child_idx + 1 == metrics.child_count);
format_element(elem, child_metrics, depth + 1, !is_last);
child_idx++;
}
if (!empty) {
format_.print_newline();
format_.print_indents(depth);
}
format_.end_array();
}
inline void fractured_string_builder::format_object(const dom::object& obj,
const element_metrics& metrics,
size_t depth,
bool has_trailing_comma) {
layout_mode layout = structure_analyzer::decide_layout(metrics, depth, options_, has_trailing_comma);
if (layout == layout_mode::single_line) {
format_object_inline(obj, metrics);
} else {
format_object_expanded(obj, metrics, depth);
}
}
inline void fractured_string_builder::format_object_inline(const dom::object& obj,
const element_metrics& metrics) {
scoped_single_line_mode single_line(format_);
format_.start_object();
bool empty = true;
bool first = true;
size_t child_idx = 0;
for (dom::key_value_pair field : obj) {
empty = false;
if (!first) {
format_.comma();
if (options_.comma_padding) {
format_.print_space();
}
} else if (bracket_padding_for(metrics)) {
format_.print_space();
}
first = false;
format_.key(field.key);
if (options_.colon_padding) {
format_.print_space();
}
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
format_element(field.value, child_metrics, 0);
child_idx++;
}
if (bracket_padding_for(metrics) && !empty) {
format_.print_space();
}
format_.end_object();
}
inline void fractured_string_builder::format_object_expanded(const dom::object& obj,
const element_metrics& metrics,
size_t depth) {
format_.start_object();
bool empty = true;
bool first = true;
size_t child_idx = 0;
for (dom::key_value_pair field : obj) {
empty = false;
if (!first) {
format_.comma();
}
first = false;
format_.print_newline();
format_.print_indents(depth + 1);
format_.key(field.key);
if (options_.colon_padding) {
format_.print_space();
}
const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
bool is_last = (child_idx + 1 == metrics.child_count);
format_element(field.value, child_metrics, depth + 1, !is_last);
child_idx++;
}
if (!empty) {
format_.print_newline();
format_.print_indents(depth);
}
format_.end_object();
}
inline void fractured_string_builder::format_scalar(const dom::element& elem) {
switch (elem.type()) {
case dom::element_type::STRING: {
std::string_view str;
if (elem.get_string().get(str) == SUCCESS) {
format_.string(str);
}
break;
}
case dom::element_type::INT64: {
int64_t val;
if (elem.get_int64().get(val) == SUCCESS) {
format_.number(val);
}
break;
}
case dom::element_type::UINT64: {
uint64_t val;
if (elem.get_uint64().get(val) == SUCCESS) {
format_.number(val);
}
break;
}
case dom::element_type::DOUBLE: {
double val;
if (elem.get_double().get(val) == SUCCESS) {
format_.number(val);
}
break;
}
case dom::element_type::BOOL: {
bool val;
if (elem.get_bool().get(val) == SUCCESS) {
val ? format_.true_atom() : format_.false_atom();
}
break;
}
case dom::element_type::NULL_VALUE:
format_.null_atom();
break;
default:
break;
}
}
inline bool fractured_string_builder::bracket_padding_for(const element_metrics& metrics) const {
return metrics.complexity >= 2 ? options_.nested_bracket_padding : options_.simple_bracket_padding;
}
} // namespace internal
//
// Public API Implementation
//
template <class T>
std::string fractured_json(T x) {
return fractured_json(x, fractured_json_options{});
}
template <class T>
std::string fractured_json(T x, const fractured_json_options& options) {
internal::fractured_string_builder sb(options);
sb.append(x);
std::string_view result = sb.str();
return std::string(result.data(), result.size());
}
#if SIMDJSON_EXCEPTIONS
template <class T>
std::string fractured_json(simdjson_result<T> x) {
if (x.error()) {
throw simdjson_error(x.error());
}
return fractured_json(x.value());
}
template <class T>
std::string fractured_json(simdjson_result<T> x, const fractured_json_options& options) {
if (x.error()) {
throw simdjson_error(x.error());
}
return fractured_json(x.value(), options);
}
#endif
//
// String-based API for formatting any JSON string
//
inline std::string fractured_json_string(std::string_view json_str) {
return fractured_json_string(json_str, fractured_json_options{});
}
inline std::string fractured_json_string(std::string_view json_str,
const fractured_json_options& options) {
// Parse the JSON string
dom::parser parser;
dom::element doc;
// Need to pad the string for simdjson
auto padded = padded_string(json_str);
auto error = parser.parse(padded).get(doc);
if (error) {
// If parsing fails, return the original string
return std::string(json_str);
}
return fractured_json(doc, options);
}
} // namespace simdjson
#endif // SIMDJSON_DOM_FRACTURED_JSON_INL_H
/* end file simdjson/dom/fractured_json-inl.h */
#endif // SIMDJSON_DOM_H
/* end file simdjson/dom.h */
/* including simdjson/builder.h: #include "simdjson/builder.h" */
/* begin file simdjson/builder.h */
#ifndef SIMDJSON_BUILDER_H
#define SIMDJSON_BUILDER_H
/* including simdjson/builtin/builder.h: #include "simdjson/builtin/builder.h" */
/* begin file simdjson/builtin/builder.h */
#ifndef SIMDJSON_BUILTIN_BUILDER_H
#define SIMDJSON_BUILTIN_BUILDER_H
/* including simdjson/builtin.h: #include "simdjson/builtin.h" */
/* begin file simdjson/builtin.h */
#ifndef SIMDJSON_BUILTIN_H
#define SIMDJSON_BUILTIN_H
/* including simdjson/builtin/base.h: #include "simdjson/builtin/base.h" */
/* begin file simdjson/builtin/base.h */
#ifndef SIMDJSON_BUILTIN_BASE_H
#define SIMDJSON_BUILTIN_BASE_H
/* skipped duplicate #include "simdjson/base.h" */
/* including simdjson/implementation_detection.h: #include "simdjson/implementation_detection.h" */
/* begin file simdjson/implementation_detection.h */
#ifndef SIMDJSON_IMPLEMENTATION_DETECTION_H
#define SIMDJSON_IMPLEMENTATION_DETECTION_H
/* skipped duplicate #include "simdjson/base.h" */
// 0 is reserved, because undefined SIMDJSON_IMPLEMENTATION equals 0 in preprocessor macros.
#define SIMDJSON_IMPLEMENTATION_ID_arm64 1
#define SIMDJSON_IMPLEMENTATION_ID_fallback 2
#define SIMDJSON_IMPLEMENTATION_ID_haswell 3
#define SIMDJSON_IMPLEMENTATION_ID_icelake 4
#define SIMDJSON_IMPLEMENTATION_ID_ppc64 5
#define SIMDJSON_IMPLEMENTATION_ID_westmere 6
#define SIMDJSON_IMPLEMENTATION_ID_lsx 7
#define SIMDJSON_IMPLEMENTATION_ID_lasx 8
//#define SIMDJSON_IMPLEMENTATION_ID_rvv 9
#define SIMDJSON_IMPLEMENTATION_ID_rvv_vls 10
#define SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) SIMDJSON_CAT(SIMDJSON_IMPLEMENTATION_ID_, IMPL)
#define SIMDJSON_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_IMPLEMENTATION)
#define SIMDJSON_IMPLEMENTATION_IS(IMPL) SIMDJSON_IMPLEMENTATION_ID == SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL)
//
// First, figure out which implementations can be run. Doing it here makes it so we don't have to worry about the order
// in which we include them.
//
#ifndef SIMDJSON_IMPLEMENTATION_ARM64
#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64)
#endif
#if SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 1
#else
#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 0
#endif
// Default Icelake to on if this is x86-64. Even if we're not compiled for it, it could be selected
// at runtime.
#ifndef SIMDJSON_IMPLEMENTATION_ICELAKE
#define SIMDJSON_IMPLEMENTATION_ICELAKE ((SIMDJSON_IS_X86_64) && (SIMDJSON_AVX512_ALLOWED) && (SIMDJSON_COMPILER_SUPPORTS_VBMI2))
#endif
#ifdef _MSC_VER
// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see
// https://github.com/simdjson/simdjson/issues/1247
#if ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 1
#else
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 0
#endif
#else
#if ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__))
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 1
#else
#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE 0
#endif
#endif
// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected
// at runtime.
#ifndef SIMDJSON_IMPLEMENTATION_HASWELL
#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE
// if icelake is always available, never enable haswell.
#define SIMDJSON_IMPLEMENTATION_HASWELL 0
#else
#define SIMDJSON_IMPLEMENTATION_HASWELL SIMDJSON_IS_X86_64
#endif
#endif
#ifdef _MSC_VER
// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see
// https://github.com/simdjson/simdjson/issues/1247
#if ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__))
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 1
#else
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 0
#endif
#else
#if ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__))
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 1
#else
#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL 0
#endif
#endif
// Default Westmere to on if this is x86-64.
#ifndef SIMDJSON_IMPLEMENTATION_WESTMERE
#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL
// if icelake or haswell are always available, never enable westmere.
#define SIMDJSON_IMPLEMENTATION_WESTMERE 0
#else
#define SIMDJSON_IMPLEMENTATION_WESTMERE SIMDJSON_IS_X86_64
#endif
#endif
#if (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__)
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE 1
#else
#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE 0
#endif
#ifndef SIMDJSON_IMPLEMENTATION_PPC64
#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX)
#endif
#if SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 && SIMDJSON_IS_PPC64_VMX
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 1
#else
#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 0
#endif
#ifndef SIMDJSON_IMPLEMENTATION_LASX
#define SIMDJSON_IMPLEMENTATION_LASX (SIMDJSON_IS_LSX)
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_LASX (SIMDJSON_IS_LASX)
#ifndef SIMDJSON_IMPLEMENTATION_LSX
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
#define SIMDJSON_IMPLEMENTATION_LSX 0
#else
#define SIMDJSON_IMPLEMENTATION_LSX (SIMDJSON_IS_LSX)
#endif
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_LSX (SIMDJSON_IMPLEMENTATION_LSX)
#define SIMDJSON_CAN_ALWAYS_RUN_RVV_VLS SIMDJSON_IS_RVV_VLS
#ifndef SIMDJSON_IMPLEMENTATION_RVV_VLS
#define SIMDJSON_IMPLEMENTATION_RVV_VLS SIMDJSON_CAN_ALWAYS_RUN_RVV_VLS
#endif
// Default Fallback to on unless a builtin implementation has already been selected.
#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64 || SIMDJSON_CAN_ALWAYS_RUN_LSX || SIMDJSON_CAN_ALWAYS_RUN_LASX || SIMDJSON_CAN_ALWAYS_RUN_RVV_VLS
// if anything at all except fallback can always run, then disable fallback.
#define SIMDJSON_IMPLEMENTATION_FALLBACK 0
#else
#define SIMDJSON_IMPLEMENTATION_FALLBACK 1
#endif
#endif
#define SIMDJSON_CAN_ALWAYS_RUN_FALLBACK SIMDJSON_IMPLEMENTATION_FALLBACK
// Determine the best builtin implementation
#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION
#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE
#define SIMDJSON_BUILTIN_IMPLEMENTATION icelake
#elif SIMDJSON_CAN_ALWAYS_RUN_HASWELL
#define SIMDJSON_BUILTIN_IMPLEMENTATION haswell
#elif SIMDJSON_CAN_ALWAYS_RUN_WESTMERE
#define SIMDJSON_BUILTIN_IMPLEMENTATION westmere
#elif SIMDJSON_CAN_ALWAYS_RUN_ARM64
#define SIMDJSON_BUILTIN_IMPLEMENTATION arm64
#elif SIMDJSON_CAN_ALWAYS_RUN_PPC64
#define SIMDJSON_BUILTIN_IMPLEMENTATION ppc64
#elif SIMDJSON_CAN_ALWAYS_RUN_LSX
#define SIMDJSON_BUILTIN_IMPLEMENTATION lsx
#elif SIMDJSON_CAN_ALWAYS_RUN_LASX
#define SIMDJSON_BUILTIN_IMPLEMENTATION lasx
#elif SIMDJSON_CAN_ALWAYS_RUN_RVV_VLS
#define SIMDJSON_BUILTIN_IMPLEMENTATION rvv_vls
#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK
#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback
#else
#error "All possible implementations (including fallback) have been disabled! simdjson will not run."
#endif
#endif // SIMDJSON_BUILTIN_IMPLEMENTATION
#define SIMDJSON_BUILTIN_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_BUILTIN_IMPLEMENTATION)
#define SIMDJSON_BUILTIN_IMPLEMENTATION_IS(IMPL) SIMDJSON_BUILTIN_IMPLEMENTATION_ID == SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL)
#endif // SIMDJSON_IMPLEMENTATION_DETECTION_H
/* end file simdjson/implementation_detection.h */
namespace simdjson {
#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64)
namespace arm64 {}
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback)
namespace fallback {}
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell)
namespace haswell {}
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake)
namespace icelake {}
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64)
namespace ppc64 {}
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
namespace westmere {}
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
namespace lsx {}
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
namespace lasx {}
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
namespace rvv_vls {}
#else
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
#endif
/**
* Represents the best statically linked simdjson implementation that can be used by the compiling
* program.
*
* Detects what options the program is compiled against, and picks the minimum implementation that
* will work on any computer that can run the program. For example, if you compile with g++
* -march=westmere, it will pick the westmere implementation. The haswell implementation will
* still be available, and can be selected at runtime, but the builtin implementation (and any
* code that uses it) will use westmere.
*/
namespace builtin = SIMDJSON_BUILTIN_IMPLEMENTATION;
} // namespace simdjson
#endif // SIMDJSON_BUILTIN_BASE_H
/* end file simdjson/builtin/base.h */
/* including simdjson/builtin/implementation.h: #include "simdjson/builtin/implementation.h" */
/* begin file simdjson/builtin/implementation.h */
#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION_H
#define SIMDJSON_BUILTIN_IMPLEMENTATION_H
/* skipped duplicate #include "simdjson/builtin/base.h" */
/* including simdjson/generic/dependencies.h: #include "simdjson/generic/dependencies.h" */
/* begin file simdjson/generic/dependencies.h */
#ifdef SIMDJSON_CONDITIONAL_INCLUDE
#error simdjson/generic/dependencies.h must be included before defining SIMDJSON_CONDITIONAL_INCLUDE!
#endif
#ifndef SIMDJSON_GENERIC_DEPENDENCIES_H
#define SIMDJSON_GENERIC_DEPENDENCIES_H
// Internal headers needed for generics.
// All includes referencing simdjson headers *not* under simdjson/generic must be here!
// Otherwise, amalgamation will fail.
/* skipped duplicate #include "simdjson/base.h" */
/* skipped duplicate #include "simdjson/implementation.h" */
/* skipped duplicate #include "simdjson/implementation_detection.h" */
/* including simdjson/internal/instruction_set.h: #include "simdjson/internal/instruction_set.h" */
/* begin file simdjson/internal/instruction_set.h */
/* From
https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h
Highly modified.
Copyright (c) 2016- Facebook, Inc (Adam Paszke)
Copyright (c) 2014- Facebook, Inc (Soumith Chintala)
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
Copyright (c) 2011-2013 NYU (Clement Farabet)
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou,
Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute
(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert,
Samy Bengio, Johnny Mariethoz)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories
America and IDIAP Research Institute nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SIMDJSON_INTERNAL_INSTRUCTION_SET_H
#define SIMDJSON_INTERNAL_INSTRUCTION_SET_H
namespace simdjson {
namespace internal {
enum instruction_set {
DEFAULT = 0x0,
NEON = 0x1,
AVX2 = 0x4,
SSE42 = 0x8,
PCLMULQDQ = 0x10,
BMI1 = 0x20,
BMI2 = 0x40,
ALTIVEC = 0x80,
AVX512F = 0x100,
AVX512DQ = 0x200,
AVX512IFMA = 0x400,
AVX512PF = 0x800,
AVX512ER = 0x1000,
AVX512CD = 0x2000,
AVX512BW = 0x4000,
AVX512VL = 0x8000,
AVX512VBMI2 = 0x10000,
LSX = 0x20000,
LASX = 0x40000,
//RVV = 0x80000,
RVV_VLS = 0x100000,
SVE = 0x200000,
SVE2 = 0x400000,
};
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_INSTRUCTION_SET_H
/* end file simdjson/internal/instruction_set.h */
/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */
/* including simdjson/internal/jsoncharutils_tables.h: #include "simdjson/internal/jsoncharutils_tables.h" */
/* begin file simdjson/internal/jsoncharutils_tables.h */
#ifndef SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H
#define SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H
/* skipped duplicate #include "simdjson/base.h" */
#ifdef JSON_TEST_STRINGS
void found_string(const uint8_t *buf, const uint8_t *parsed_begin,
const uint8_t *parsed_end);
void found_bad_string(const uint8_t *buf);
#endif
namespace simdjson {
namespace internal {
// structural chars here are
// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL)
// we are also interested in the four whitespace characters
// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d
extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace_negated[256];
extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace[256];
extern SIMDJSON_DLLIMPORTEXPORT const uint32_t digit_to_val32[886];
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H
/* end file simdjson/internal/jsoncharutils_tables.h */
/* including simdjson/internal/numberparsing_tables.h: #include "simdjson/internal/numberparsing_tables.h" */
/* begin file simdjson/internal/numberparsing_tables.h */
#ifndef SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H
#define SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H
/* skipped duplicate #include "simdjson/base.h" */
namespace simdjson {
namespace internal {
/**
* The smallest non-zero float (binary64) is 2^-1074.
* We take as input numbers of the form w x 10^q where w < 2^64.
* We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
* However, we have that
* (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074.
* Thus it is possible for a number of the form w * 10^-342 where
* w is a 64-bit value to be a non-zero floating-point number.
*********
* Any number of form w * 10^309 where w>= 1 is going to be
* infinite in binary64 so we never need to worry about powers
* of 5 greater than 308.
*/
constexpr int smallest_power = -342;
constexpr int largest_power = 308;
/**
* Represents a 128-bit value.
* low: least significant 64 bits.
* high: most significant 64 bits.
*/
struct value128 {
uint64_t low;
uint64_t high;
};
// Precomputed powers of ten from 10^0 to 10^22. These
// can be represented exactly using the double type.
extern SIMDJSON_DLLIMPORTEXPORT const double power_of_ten[];
/**
* When mapping numbers from decimal to binary,
* we go from w * 10^q to m * 2^p but we have
* 10^q = 5^q * 2^q, so effectively
* we are trying to match
* w * 2^q * 5^q to m * 2^p. Thus the powers of two
* are not a concern since they can be represented
* exactly using the binary notation, only the powers of five
* affect the binary significand.
*/
// The truncated powers of five from 5^-342 all the way to 5^308
// The mantissa is truncated to 128 bits, and
// never rounded up. Uses about 10KB.
// We use the template trick to allow inclusion in multiple translation units.
#if SIMDJSON_STATIC_REFLECTION
template <typename unused = void> struct powers_template {
constexpr static uint64_t power_of_five_128[651*2]= {
0xeef453d6923bd65a,0x113faa2906a13b3f,
0x9558b4661b6565f8,0x4ac7ca59a424c507,
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
0xe95a99df8ace6f53,0xf4d82c2c107973dc,
0x91d8a02bb6c10594,0x79071b9b8a4be869,
0xb64ec836a47146f9,0x9748e2826cdee284,
0xe3e27a444d8d98b7,0xfd1b1b2308169b25,
0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7,
0xb208ef855c969f4f,0xbdbd2d335e51a935,
0xde8b2b66b3bc4723,0xad2c788035e61382,
0x8b16fb203055ac76,0x4c3bcb5021afcc31,
0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d,
0xd953e8624b85dd78,0xd71d6dad34a2af0d,
0x87d4713d6f33aa6b,0x8672648c40e5ad68,
0xa9c98d8ccb009506,0x680efdaf511f18c2,
0xd43bf0effdc0ba48,0x212bd1b2566def2,
0x84a57695fe98746d,0x14bb630f7604b57,
0xa5ced43b7e3e9188,0x419ea3bd35385e2d,
0xcf42894a5dce35ea,0x52064cac828675b9,
0x818995ce7aa0e1b2,0x7343efebd1940993,
0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8,
0xca66fa129f9b60a6,0xd41a26e077774ef6,
0xfd00b897478238d0,0x8920b098955522b4,
0x9e20735e8cb16382,0x55b46e5f5d5535b0,
0xc5a890362fddbc62,0xeb2189f734aa831d,
0xf712b443bbd52b7b,0xa5e9ec7501d523e4,
0x9a6bb0aa55653b2d,0x47b233c92125366e,
0xc1069cd4eabe89f8,0x999ec0bb696e840a,
0xf148440a256e2c76,0xc00670ea43ca250d,
0x96cd2a865764dbca,0x380406926a5e5728,
0xbc807527ed3e12bc,0xc605083704f5ecf2,
0xeba09271e88d976b,0xf7864a44c633682e,
0x93445b8731587ea3,0x7ab3ee6afbe0211d,
0xb8157268fdae9e4c,0x5960ea05bad82964,
0xe61acf033d1a45df,0x6fb92487298e33bd,
0x8fd0c16206306bab,0xa5d3b6d479f8e056,
0xb3c4f1ba87bc8696,0x8f48a4899877186c,
0xe0b62e2929aba83c,0x331acdabfe94de87,
0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14,
0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9,
0xdb71e91432b1a24a,0xc9e82cd9f69d6150,
0x892731ac9faf056e,0xbe311c083a225cd2,
0xab70fe17c79ac6ca,0x6dbd630a48aaf406,
0xd64d3d9db981787d,0x92cbbccdad5b108,
0x85f0468293f0eb4e,0x25bbf56008c58ea5,
0xa76c582338ed2621,0xaf2af2b80af6f24e,
0xd1476e2c07286faa,0x1af5af660db4aee1,
0x82cca4db847945ca,0x50d98d9fc890ed4d,
0xa37fce126597973c,0xe50ff107bab528a0,
0xcc5fc196fefd7d0c,0x1e53ed49a96272c8,
0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a,
0x9faacf3df73609b1,0x77b191618c54e9ac,
0xc795830d75038c1d,0xd59df5b9ef6a2417,
0xf97ae3d0d2446f25,0x4b0573286b44ad1d,
0x9becce62836ac577,0x4ee367f9430aec32,
0xc2e801fb244576d5,0x229c41f793cda73f,
0xf3a20279ed56d48a,0x6b43527578c1110f,
0x9845418c345644d6,0x830a13896b78aaa9,
0xbe5691ef416bd60c,0x23cc986bc656d553,
0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8,
0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9,
0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53,
0xe858ad248f5c22c9,0xd1b3400f8f9cff68,
0x91376c36d99995be,0x23100809b9c21fa1,
0xb58547448ffffb2d,0xabd40a0c2832a78a,
0xe2e69915b3fff9f9,0x16c90c8f323f516c,
0x8dd01fad907ffc3b,0xae3da7d97f6792e3,
0xb1442798f49ffb4a,0x99cd11cfdf41779c,
0xdd95317f31c7fa1d,0x40405643d711d583,
0x8a7d3eef7f1cfc52,0x482835ea666b2572,
0xad1c8eab5ee43b66,0xda3243650005eecf,
0xd863b256369d4a40,0x90bed43e40076a82,
0x873e4f75e2224e68,0x5a7744a6e804a291,
0xa90de3535aaae202,0x711515d0a205cb36,
0xd3515c2831559a83,0xd5a5b44ca873e03,
0x8412d9991ed58091,0xe858790afe9486c2,
0xa5178fff668ae0b6,0x626e974dbe39a872,
0xce5d73ff402d98e3,0xfb0a3d212dc8128f,
0x80fa687f881c7f8e,0x7ce66634bc9d0b99,
0xa139029f6a239f72,0x1c1fffc1ebc44e80,
0xc987434744ac874e,0xa327ffb266b56220,
0xfbe9141915d7a922,0x4bf1ff9f0062baa8,
0x9d71ac8fada6c9b5,0x6f773fc3603db4a9,
0xc4ce17b399107c22,0xcb550fb4384d21d3,
0xf6019da07f549b2b,0x7e2a53a146606a48,
0x99c102844f94e0fb,0x2eda7444cbfc426d,
0xc0314325637a1939,0xfa911155fefb5308,
0xf03d93eebc589f88,0x793555ab7eba27ca,
0x96267c7535b763b5,0x4bc1558b2f3458de,
0xbbb01b9283253ca2,0x9eb1aaedfb016f16,
0xea9c227723ee8bcb,0x465e15a979c1cadc,
0x92a1958a7675175f,0xbfacd89ec191ec9,
0xb749faed14125d36,0xcef980ec671f667b,
0xe51c79a85916f484,0x82b7e12780e7401a,
0x8f31cc0937ae58d2,0xd1b2ecb8b0908810,
0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15,
0xdfbdcece67006ac9,0x67a791e093e1d49a,
0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0,
0xaecc49914078536d,0x58fae9f773886e18,
0xda7f5bf590966848,0xaf39a475506a899e,
0x888f99797a5e012d,0x6d8406c952429603,
0xaab37fd7d8f58178,0xc8e5087ba6d33b83,
0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64,
0x855c3be0a17fcd26,0x5cf2eea09a55067f,
0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e,
0xd0601d8efc57b08b,0xf13b94daf124da26,
0x823c12795db6ce57,0x76c53d08d6b70858,
0xa2cb1717b52481ed,0x54768c4b0c64ca6e,
0xcb7ddcdda26da268,0xa9942f5dcf7dfd09,
0xfe5d54150b090b02,0xd3f93b35435d7c4c,
0x9efa548d26e5a6e1,0xc47bc5014a1a6daf,
0xc6b8e9b0709f109a,0x359ab6419ca1091b,
0xf867241c8cc6d4c0,0xc30163d203c94b62,
0x9b407691d7fc44f8,0x79e0de63425dcf1d,
0xc21094364dfb5636,0x985915fc12f542e4,
0xf294b943e17a2bc4,0x3e6f5b7b17b2939d,
0x979cf3ca6cec5b5a,0xa705992ceecf9c42,
0xbd8430bd08277231,0x50c6ff782a838353,
0xece53cec4a314ebd,0xa4f8bf5635246428,
0x940f4613ae5ed136,0x871b7795e136be99,
0xb913179899f68584,0x28e2557b59846e3f,
0xe757dd7ec07426e5,0x331aeada2fe589cf,
0x9096ea6f3848984f,0x3ff0d2c85def7621,
0xb4bca50b065abe63,0xfed077a756b53a9,
0xe1ebce4dc7f16dfb,0xd3e8495912c62894,
0x8d3360f09cf6e4bd,0x64712dd7abbbd95c,
0xb080392cc4349dec,0xbd8d794d96aacfb3,
0xdca04777f541c567,0xecf0d7a0fc5583a0,
0x89e42caaf9491b60,0xf41686c49db57244,
0xac5d37d5b79b6239,0x311c2875c522ced5,
0xd77485cb25823ac7,0x7d633293366b828b,
0x86a8d39ef77164bc,0xae5dff9c02033197,
0xa8530886b54dbdeb,0xd9f57f830283fdfc,
0xd267caa862a12d66,0xd072df63c324fd7b,
0x8380dea93da4bc60,0x4247cb9e59f71e6d,
0xa46116538d0deb78,0x52d9be85f074e608,
0xcd795be870516656,0x67902e276c921f8b,
0x806bd9714632dff6,0xba1cd8a3db53b6,
0xa086cfcd97bf97f3,0x80e8a40eccd228a4,
0xc8a883c0fdaf7df0,0x6122cd128006b2cd,
0xfad2a4b13d1b5d6c,0x796b805720085f81,
0x9cc3a6eec6311a63,0xcbe3303674053bb0,
0xc3f490aa77bd60fc,0xbedbfc4411068a9c,
0xf4f1b4d515acb93b,0xee92fb5515482d44,
0x991711052d8bf3c5,0x751bdd152d4d1c4a,
0xbf5cd54678eef0b6,0xd262d45a78a0635d,
0xef340a98172aace4,0x86fb897116c87c34,
0x9580869f0e7aac0e,0xd45d35e6ae3d4da0,
0xbae0a846d2195712,0x8974836059cca109,
0xe998d258869facd7,0x2bd1a438703fc94b,
0x91ff83775423cc06,0x7b6306a34627ddcf,
0xb67f6455292cbf08,0x1a3bc84c17b1d542,
0xe41f3d6a7377eeca,0x20caba5f1d9e4a93,
0x8e938662882af53e,0x547eb47b7282ee9c,
0xb23867fb2a35b28d,0xe99e619a4f23aa43,
0xdec681f9f4c31f31,0x6405fa00e2ec94d4,
0x8b3c113c38f9f37e,0xde83bc408dd3dd04,
0xae0b158b4738705e,0x9624ab50b148d445,
0xd98ddaee19068c76,0x3badd624dd9b0957,
0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6,
0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c,
0xd47487cc8470652b,0x7647c3200069671f,
0x84c8d4dfd2c63f3b,0x29ecd9f40041e073,
0xa5fb0a17c777cf09,0xf468107100525890,
0xcf79cc9db955c2cc,0x7182148d4066eeb4,
0x81ac1fe293d599bf,0xc6f14cd848405530,
0xa21727db38cb002f,0xb8ada00e5a506a7c,
0xca9cf1d206fdc03b,0xa6d90811f0e4851c,
0xfd442e4688bd304a,0x908f4a166d1da663,
0x9e4a9cec15763e2e,0x9a598e4e043287fe,
0xc5dd44271ad3cdba,0x40eff1e1853f29fd,
0xf7549530e188c128,0xd12bee59e68ef47c,
0x9a94dd3e8cf578b9,0x82bb74f8301958ce,
0xc13a148e3032d6e7,0xe36a52363c1faf01,
0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1,
0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9,
0xbcb2b812db11a5de,0x7415d448f6b6f0e7,
0xebdf661791d60f56,0x111b495b3464ad21,
0x936b9fcebb25c995,0xcab10dd900beec34,
0xb84687c269ef3bfb,0x3d5d514f40eea742,
0xe65829b3046b0afa,0xcb4a5a3112a5112,
0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab,
0xb3f4e093db73a093,0x59ed216765690f56,
0xe0f218b8d25088b8,0x306869c13ec3532c,
0x8c974f7383725573,0x1e414218c73a13fb,
0xafbd2350644eeacf,0xe5d1929ef90898fa,
0xdbac6c247d62a583,0xdf45f746b74abf39,
0x894bc396ce5da772,0x6b8bba8c328eb783,
0xab9eb47c81f5114f,0x66ea92f3f326564,
0xd686619ba27255a2,0xc80a537b0efefebd,
0x8613fd0145877585,0xbd06742ce95f5f36,
0xa798fc4196e952e7,0x2c48113823b73704,
0xd17f3b51fca3a7a0,0xf75a15862ca504c5,
0x82ef85133de648c4,0x9a984d73dbe722fb,
0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba,
0xcc963fee10b7d1b3,0x318df905079926a8,
0xffbbcfe994e5c61f,0xfdf17746497f7052,
0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633,
0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0,
0xf9bd690a1b68637b,0x3dfdce7aa3c673b0,
0x9c1661a651213e2d,0x6bea10ca65c084e,
0xc31bfa0fe5698db8,0x486e494fcff30a62,
0xf3e2f893dec3f126,0x5a89dba3c3efccfa,
0x986ddb5c6b3a76b7,0xf89629465a75e01c,
0xbe89523386091465,0xf6bbb397f1135823,
0xee2ba6c0678b597f,0x746aa07ded582e2c,
0x94db483840b717ef,0xa8c2a44eb4571cdc,
0xba121a4650e4ddeb,0x92f34d62616ce413,
0xe896a0d7e51e1566,0x77b020baf9c81d17,
0x915e2486ef32cd60,0xace1474dc1d122e,
0xb5b5ada8aaff80b8,0xd819992132456ba,
0xe3231912d5bf60e6,0x10e1fff697ed6c69,
0x8df5efabc5979c8f,0xca8d3ffa1ef463c1,
0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2,
0xddd0467c64bce4a0,0xac7cb3f6d05ddbde,
0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b,
0xad4ab7112eb3929d,0x86c16c98d2c953c6,
0xd89d64d57a607744,0xe871c7bf077ba8b7,
0x87625f056c7c4a8b,0x11471cd764ad4972,
0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf,
0xd389b47879823479,0x4aff1d108d4ec2c3,
0x843610cb4bf160cb,0xcedf722a585139ba,
0xa54394fe1eedb8fe,0xc2974eb4ee658828,
0xce947a3da6a9273e,0x733d226229feea32,
0x811ccc668829b887,0x806357d5a3f525f,
0xa163ff802a3426a8,0xca07c2dcb0cf26f7,
0xc9bcff6034c13052,0xfc89b393dd02f0b5,
0xfc2c3f3841f17c67,0xbbac2078d443ace2,
0x9d9ba7832936edc0,0xd54b944b84aa4c0d,
0xc5029163f384a931,0xa9e795e65d4df11,
0xf64335bcf065d37d,0x4d4617b5ff4a16d5,
0x99ea0196163fa42e,0x504bced1bf8e4e45,
0xc06481fb9bcf8d39,0xe45ec2862f71e1d6,
0xf07da27a82c37088,0x5d767327bb4e5a4c,
0x964e858c91ba2655,0x3a6a07f8d510f86f,
0xbbe226efb628afea,0x890489f70a55368b,
0xeadab0aba3b2dbe5,0x2b45ac74ccea842e,
0x92c8ae6b464fc96f,0x3b0b8bc90012929d,
0xb77ada0617e3bbcb,0x9ce6ebb40173744,
0xe55990879ddcaabd,0xcc420a6a101d0515,
0x8f57fa54c2a9eab6,0x9fa946824a12232d,
0xb32df8e9f3546564,0x47939822dc96abf9,
0xdff9772470297ebd,0x59787e2b93bc56f7,
0x8bfbea76c619ef36,0x57eb4edb3c55b65a,
0xaefae51477a06b03,0xede622920b6b23f1,
0xdab99e59958885c4,0xe95fab368e45eced,
0x88b402f7fd75539b,0x11dbcb0218ebb414,
0xaae103b5fcd2a881,0xd652bdc29f26a119,
0xd59944a37c0752a2,0x4be76d3346f0495f,
0x857fcae62d8493a5,0x6f70a4400c562ddb,
0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952,
0xd097ad07a71f26b2,0x7e2000a41346a7a7,
0x825ecc24c873782f,0x8ed400668c0c28c8,
0xa2f67f2dfa90563b,0x728900802f0f32fa,
0xcbb41ef979346bca,0x4f2b40a03ad2ffb9,
0xfea126b7d78186bc,0xe2f610c84987bfa8,
0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9,
0xc6ede63fa05d3143,0x91503d1c79720dbb,
0xf8a95fcf88747d94,0x75a44c6397ce912a,
0x9b69dbe1b548ce7c,0xc986afbe3ee11aba,
0xc24452da229b021b,0xfbe85badce996168,
0xf2d56790ab41c2a2,0xfae27299423fb9c3,
0x97c560ba6b0919a5,0xdccd879fc967d41a,
0xbdb6b8e905cb600f,0x5400e987bbc1c920,
0xed246723473e3813,0x290123e9aab23b68,
0x9436c0760c86e30b,0xf9a0b6720aaf6521,
0xb94470938fa89bce,0xf808e40e8d5b3e69,
0xe7958cb87392c2c2,0xb60b1d1230b20e04,
0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2,
0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3,
0xe2280b6c20dd5232,0x25c6da63c38de1b0,
0x8d590723948a535f,0x579c487e5a38ad0e,
0xb0af48ec79ace837,0x2d835a9df0c6d851,
0xdcdb1b2798182244,0xf8e431456cf88e65,
0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff,
0xac8b2d36eed2dac5,0xe272467e3d222f3f,
0xd7adf884aa879177,0x5b0ed81dcc6abb0f,
0x86ccbb52ea94baea,0x98e947129fc2b4e9,
0xa87fea27a539e9a5,0x3f2398d747b36224,
0xd29fe4b18e88640e,0x8eec7f0d19a03aad,
0x83a3eeeef9153e89,0x1953cf68300424ac,
0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7,
0xcdb02555653131b6,0x3792f412cb06794d,
0x808e17555f3ebf11,0xe2bbd88bbee40bd0,
0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4,
0xc8de047564d20a8b,0xf245825a5a445275,
0xfb158592be068d2e,0xeed6e2f0f0d56712,
0x9ced737bb6c4183d,0x55464dd69685606b,
0xc428d05aa4751e4c,0xaa97e14c3c26b886,
0xf53304714d9265df,0xd53dd99f4b3066a8,
0x993fe2c6d07b7fab,0xe546a8038efe4029,
0xbf8fdb78849a5f96,0xde98520472bdd033,
0xef73d256a5c0f77c,0x963e66858f6d4440,
0x95a8637627989aad,0xdde7001379a44aa8,
0xbb127c53b17ec159,0x5560c018580d5d52,
0xe9d71b689dde71af,0xaab8f01e6e10b4a6,
0x9226712162ab070d,0xcab3961304ca70e8,
0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22,
0xe45c10c42a2b3b05,0x8cb89a7db77c506a,
0x8eb98a7a9a5b04e3,0x77f3608e92adb242,
0xb267ed1940f1c61c,0x55f038b237591ed3,
0xdf01e85f912e37a3,0x6b6c46dec52f6688,
0x8b61313bbabce2c6,0x2323ac4b3b3da015,
0xae397d8aa96c1b77,0xabec975e0a0d081a,
0xd9c7dced53c72255,0x96e7bd358c904a21,
0x881cea14545c7575,0x7e50d64177da2e54,
0xaa242499697392d2,0xdde50bd1d5d0b9e9,
0xd4ad2dbfc3d07787,0x955e4ec64b44e864,
0x84ec3c97da624ab4,0xbd5af13bef0b113e,
0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e,
0xcfb11ead453994ba,0x67de18eda5814af2,
0x81ceb32c4b43fcf4,0x80eacf948770ced7,
0xa2425ff75e14fc31,0xa1258379a94d028d,
0xcad2f7f5359a3b3e,0x96ee45813a04330,
0xfd87b5f28300ca0d,0x8bca9d6e188853fc,
0x9e74d1b791e07e48,0x775ea264cf55347e,
0xc612062576589dda,0x95364afe032a81a0,
0xf79687aed3eec551,0x3a83ddbd83f52210,
0x9abe14cd44753b52,0xc4926a9672793580,
0xc16d9a0095928a27,0x75b7053c0f178400,
0xf1c90080baf72cb1,0x5324c68b12dd6800,
0x971da05074da7bee,0xd3f6fc16ebca8000,
0xbce5086492111aea,0x88f4bb1ca6bd0000,
0xec1e4a7db69561a5,0x2b31e9e3d0700000,
0x9392ee8e921d5d07,0x3aff322e62600000,
0xb877aa3236a4b449,0x9befeb9fad487c3,
0xe69594bec44de15b,0x4c2ebe687989a9b4,
0x901d7cf73ab0acd9,0xf9d37014bf60a11,
0xb424dc35095cd80f,0x538484c19ef38c95,
0xe12e13424bb40e13,0x2865a5f206b06fba,
0x8cbccc096f5088cb,0xf93f87b7442e45d4,
0xafebff0bcb24aafe,0xf78f69a51539d749,
0xdbe6fecebdedd5be,0xb573440e5a884d1c,
0x89705f4136b4a597,0x31680a88f8953031,
0xabcc77118461cefc,0xfdc20d2b36ba7c3e,
0xd6bf94d5e57a42bc,0x3d32907604691b4d,
0x8637bd05af6c69b5,0xa63f9a49c2c1b110,
0xa7c5ac471b478423,0xfcf80dc33721d54,
0xd1b71758e219652b,0xd3c36113404ea4a9,
0x83126e978d4fdf3b,0x645a1cac083126ea,
0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4,
0xcccccccccccccccc,0xcccccccccccccccd,
0x8000000000000000,0x0,
0xa000000000000000,0x0,
0xc800000000000000,0x0,
0xfa00000000000000,0x0,
0x9c40000000000000,0x0,
0xc350000000000000,0x0,
0xf424000000000000,0x0,
0x9896800000000000,0x0,
0xbebc200000000000,0x0,
0xee6b280000000000,0x0,
0x9502f90000000000,0x0,
0xba43b74000000000,0x0,
0xe8d4a51000000000,0x0,
0x9184e72a00000000,0x0,
0xb5e620f480000000,0x0,
0xe35fa931a0000000,0x0,
0x8e1bc9bf04000000,0x0,
0xb1a2bc2ec5000000,0x0,
0xde0b6b3a76400000,0x0,
0x8ac7230489e80000,0x0,
0xad78ebc5ac620000,0x0,
0xd8d726b7177a8000,0x0,
0x878678326eac9000,0x0,
0xa968163f0a57b400,0x0,
0xd3c21bcecceda100,0x0,
0x84595161401484a0,0x0,
0xa56fa5b99019a5c8,0x0,
0xcecb8f27f4200f3a,0x0,
0x813f3978f8940984,0x4000000000000000,
0xa18f07d736b90be5,0x5000000000000000,
0xc9f2c9cd04674ede,0xa400000000000000,
0xfc6f7c4045812296,0x4d00000000000000,
0x9dc5ada82b70b59d,0xf020000000000000,
0xc5371912364ce305,0x6c28000000000000,
0xf684df56c3e01bc6,0xc732000000000000,
0x9a130b963a6c115c,0x3c7f400000000000,
0xc097ce7bc90715b3,0x4b9f100000000000,
0xf0bdc21abb48db20,0x1e86d40000000000,
0x96769950b50d88f4,0x1314448000000000,
0xbc143fa4e250eb31,0x17d955a000000000,
0xeb194f8e1ae525fd,0x5dcfab0800000000,
0x92efd1b8d0cf37be,0x5aa1cae500000000,
0xb7abc627050305ad,0xf14a3d9e40000000,
0xe596b7b0c643c719,0x6d9ccd05d0000000,
0x8f7e32ce7bea5c6f,0xe4820023a2000000,
0xb35dbf821ae4f38b,0xdda2802c8a800000,
0xe0352f62a19e306e,0xd50b2037ad200000,
0x8c213d9da502de45,0x4526f422cc340000,
0xaf298d050e4395d6,0x9670b12b7f410000,
0xdaf3f04651d47b4c,0x3c0cdd765f114000,
0x88d8762bf324cd0f,0xa5880a69fb6ac800,
0xab0e93b6efee0053,0x8eea0d047a457a00,
0xd5d238a4abe98068,0x72a4904598d6d880,
0x85a36366eb71f041,0x47a6da2b7f864750,
0xa70c3c40a64e6c51,0x999090b65f67d924,
0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d,
0x82818f1281ed449f,0xbff8f10e7a8921a4,
0xa321f2d7226895c7,0xaff72d52192b6a0d,
0xcbea6f8ceb02bb39,0x9bf4f8a69f764490,
0xfee50b7025c36a08,0x2f236d04753d5b4,
0x9f4f2726179a2245,0x1d762422c946590,
0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5,
0xf8ebad2b84e0d58b,0xd2e0898765a7deb2,
0x9b934c3b330c8577,0x63cc55f49f88eb2f,
0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb,
0xf316271c7fc3908a,0x8bef464e3945ef7a,
0x97edd871cfda3a56,0x97758bf0e3cbb5ac,
0xbde94e8e43d0c8ec,0x3d52eeed1cbea317,
0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd,
0x945e455f24fb1cf8,0x8fe8caa93e74ef6a,
0xb975d6b6ee39e436,0xb3e2fd538e122b44,
0xe7d34c64a9c85d44,0x60dbbca87196b616,
0x90e40fbeea1d3a4a,0xbc8955e946fe31cd,
0xb51d13aea4a488dd,0x6babab6398bdbe41,
0xe264589a4dcdab14,0xc696963c7eed2dd1,
0x8d7eb76070a08aec,0xfc1e1de5cf543ca2,
0xb0de65388cc8ada8,0x3b25a55f43294bcb,
0xdd15fe86affad912,0x49ef0eb713f39ebe,
0x8a2dbf142dfcc7ab,0x6e3569326c784337,
0xacb92ed9397bf996,0x49c2c37f07965404,
0xd7e77a8f87daf7fb,0xdc33745ec97be906,
0x86f0ac99b4e8dafd,0x69a028bb3ded71a3,
0xa8acd7c0222311bc,0xc40832ea0d68ce0c,
0xd2d80db02aabd62b,0xf50a3fa490c30190,
0x83c7088e1aab65db,0x792667c6da79e0fa,
0xa4b8cab1a1563f52,0x577001b891185938,
0xcde6fd5e09abcf26,0xed4c0226b55e6f86,
0x80b05e5ac60b6178,0x544f8158315b05b4,
0xa0dc75f1778e39d6,0x696361ae3db1c721,
0xc913936dd571c84c,0x3bc3a19cd1e38e9,
0xfb5878494ace3a5f,0x4ab48a04065c723,
0x9d174b2dcec0e47b,0x62eb0d64283f9c76,
0xc45d1df942711d9a,0x3ba5d0bd324f8394,
0xf5746577930d6500,0xca8f44ec7ee36479,
0x9968bf6abbe85f20,0x7e998b13cf4e1ecb,
0xbfc2ef456ae276e8,0x9e3fedd8c321a67e,
0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e,
0x95d04aee3b80ece5,0xbba1f1d158724a12,
0xbb445da9ca61281f,0x2a8a6e45ae8edc97,
0xea1575143cf97226,0xf52d09d71a3293bd,
0x924d692ca61be758,0x593c2626705f9c56,
0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c,
0xe498f455c38b997a,0xb6dfb9c0f956447,
0x8edf98b59a373fec,0x4724bd4189bd5eac,
0xb2977ee300c50fe7,0x58edec91ec2cb657,
0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed,
0x8b865b215899f46c,0xbd79e0d20082ee74,
0xae67f1e9aec07187,0xecd8590680a3aa11,
0xda01ee641a708de9,0xe80e6f4820cc9495,
0x884134fe908658b2,0x3109058d147fdcdd,
0xaa51823e34a7eede,0xbd4b46f0599fd415,
0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a,
0x850fadc09923329e,0x3e2cf6bc604ddb0,
0xa6539930bf6bff45,0x84db8346b786151c,
0xcfe87f7cef46ff16,0xe612641865679a63,
0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e,
0xa26da3999aef7749,0xe3be5e330f38f09d,
0xcb090c8001ab551c,0x5cadf5bfd3072cc5,
0xfdcb4fa002162a63,0x73d9732fc7c8f7f6,
0x9e9f11c4014dda7e,0x2867e7fddcdd9afa,
0xc646d63501a1511d,0xb281e1fd541501b8,
0xf7d88bc24209a565,0x1f225a7ca91a4226,
0x9ae757596946075f,0x3375788de9b06958,
0xc1a12d2fc3978937,0x52d6b1641c83ae,
0xf209787bb47d6b84,0xc0678c5dbd23a49a,
0x9745eb4d50ce6332,0xf840b7ba963646e0,
0xbd176620a501fbff,0xb650e5a93bc3d898,
0xec5d3fa8ce427aff,0xa3e51f138ab4cebe,
0x93ba47c980e98cdf,0xc66f336c36b10137,
0xb8a8d9bbe123f017,0xb80b0047445d4184,
0xe6d3102ad96cec1d,0xa60dc059157491e5,
0x9043ea1ac7e41392,0x87c89837ad68db2f,
0xb454e4a179dd1877,0x29babe4598c311fb,
0xe16a1dc9d8545e94,0xf4296dd6fef3d67a,
0x8ce2529e2734bb1d,0x1899e4a65f58660c,
0xb01ae745b101e9e4,0x5ec05dcff72e7f8f,
0xdc21a1171d42645d,0x76707543f4fa1f73,
0x899504ae72497eba,0x6a06494a791c53a8,
0xabfa45da0edbde69,0x487db9d17636892,
0xd6f8d7509292d603,0x45a9d2845d3c42b6,
0x865b86925b9bc5c2,0xb8a2392ba45a9b2,
0xa7f26836f282b732,0x8e6cac7768d7141e,
0xd1ef0244af2364ff,0x3207d795430cd926,
0x8335616aed761f1f,0x7f44e6bd49e807b8,
0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6,
0xcd036837130890a1,0x36dba887c37a8c0f,
0x802221226be55a64,0xc2494954da2c9789,
0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c,
0xc83553c5c8965d3d,0x6f92829494e5acc7,
0xfa42a8b73abbf48c,0xcb772339ba1f17f9,
0x9c69a97284b578d7,0xff2a760414536efb,
0xc38413cf25e2d70d,0xfef5138519684aba,
0xf46518c2ef5b8cd1,0x7eb258665fc25d69,
0x98bf2f79d5993802,0xef2f773ffbd97a61,
0xbeeefb584aff8603,0xaafb550ffacfd8fa,
0xeeaaba2e5dbf6784,0x95ba2a53f983cf38,
0x952ab45cfa97a0b2,0xdd945a747bf26183,
0xba756174393d88df,0x94f971119aeef9e4,
0xe912b9d1478ceb17,0x7a37cd5601aab85d,
0x91abb422ccb812ee,0xac62e055c10ab33a,
0xb616a12b7fe617aa,0x577b986b314d6009,
0xe39c49765fdf9d94,0xed5a7e85fda0b80b,
0x8e41ade9fbebc27d,0x14588f13be847307,
0xb1d219647ae6b31c,0x596eb2d8ae258fc8,
0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb,
0x8aec23d680043bee,0x25de7bb9480d5854,
0xada72ccc20054ae9,0xaf561aa79a10ae6a,
0xd910f7ff28069da4,0x1b2ba1518094da04,
0x87aa9aff79042286,0x90fb44d2f05d0842,
0xa99541bf57452b28,0x353a1607ac744a53,
0xd3fa922f2d1675f2,0x42889b8997915ce8,
0x847c9b5d7c2e09b7,0x69956135febada11,
0xa59bc234db398c25,0x43fab9837e699095,
0xcf02b2c21207ef2e,0x94f967e45e03f4bb,
0x8161afb94b44f57d,0x1d1be0eebac278f5,
0xa1ba1ba79e1632dc,0x6462d92a69731732,
0xca28a291859bbf93,0x7d7b8f7503cfdcfe,
0xfcb2cb35e702af78,0x5cda735244c3d43e,
0x9defbf01b061adab,0x3a0888136afa64a7,
0xc56baec21c7a1916,0x88aaa1845b8fdd0,
0xf6c69a72a3989f5b,0x8aad549e57273d45,
0x9a3c2087a63f6399,0x36ac54e2f678864b,
0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd,
0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5,
0x969eb7c47859e743,0x9f644ae5a4b1b325,
0xbc4665b596706114,0x873d5d9f0dde1fee,
0xeb57ff22fc0c7959,0xa90cb506d155a7ea,
0x9316ff75dd87cbd8,0x9a7f12442d588f2,
0xb7dcbf5354e9bece,0xc11ed6d538aeb2f,
0xe5d3ef282a242e81,0x8f1668c8a86da5fa,
0x8fa475791a569d10,0xf96e017d694487bc,
0xb38d92d760ec4455,0x37c981dcc395a9ac,
0xe070f78d3927556a,0x85bbe253f47b1417,
0x8c469ab843b89562,0x93956d7478ccec8e,
0xaf58416654a6babb,0x387ac8d1970027b2,
0xdb2e51bfe9d0696a,0x6997b05fcc0319e,
0x88fcf317f22241e2,0x441fece3bdf81f03,
0xab3c2fddeeaad25a,0xd527e81cad7626c3,
0xd60b3bd56a5586f1,0x8a71e223d8d3b074,
0x85c7056562757456,0xf6872d5667844e49,
0xa738c6bebb12d16c,0xb428f8ac016561db,
0xd106f86e69d785c7,0xe13336d701beba52,
0x82a45b450226b39c,0xecc0024661173473,
0xa34d721642b06084,0x27f002d7f95d0190,
0xcc20ce9bd35c78a5,0x31ec038df7b441f4,
0xff290242c83396ce,0x7e67047175a15271,
0x9f79a169bd203e41,0xf0062c6e984d386,
0xc75809c42c684dd1,0x52c07b78a3e60868,
0xf92e0c3537826145,0xa7709a56ccdf8a82,
0x9bbcc7a142b17ccb,0x88a66076400bb691,
0xc2abf989935ddbfe,0x6acff893d00ea435,
0xf356f7ebf83552fe,0x583f6b8c4124d43,
0x98165af37b2153de,0xc3727a337a8b704a,
0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c,
0xeda2ee1c7064130c,0x1162def06f79df73,
0x9485d4d1c63e8be7,0x8addcb5645ac2ba8,
0xb9a74a0637ce2ee1,0x6d953e2bd7173692,
0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437,
0x910ab1d4db9914a0,0x1d9c9892400a22a2,
0xb54d5e4a127f59c8,0x2503beb6d00cab4b,
0xe2a0b5dc971f303a,0x2e44ae64840fd61d,
0x8da471a9de737e24,0x5ceaecfed289e5d2,
0xb10d8e1456105dad,0x7425a83e872c5f47,
0xdd50f1996b947518,0xd12f124e28f77719,
0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f,
0xace73cbfdc0bfb7b,0x636cc64d1001550b,
0xd8210befd30efa5a,0x3c47f7e05401aa4e,
0x8714a775e3e95c78,0x65acfaec34810a71,
0xa8d9d1535ce3b396,0x7f1839a741a14d0d,
0xd31045a8341ca07c,0x1ede48111209a050,
0x83ea2b892091e44d,0x934aed0aab460432,
0xa4e4b66b68b65d60,0xf81da84d5617853f,
0xce1de40642e3f4b9,0x36251260ab9d668e,
0x80d2ae83e9ce78f3,0xc1d72b7c6b426019,
0xa1075a24e4421730,0xb24cf65b8612f81f,
0xc94930ae1d529cfc,0xdee033f26797b627,
0xfb9b7cd9a4a7443c,0x169840ef017da3b1,
0x9d412e0806e88aa5,0x8e1f289560ee864e,
0xc491798a08a2ad4e,0xf1a6f2bab92a27e2,
0xf5b5d7ec8acb58a2,0xae10af696774b1db,
0x9991a6f3d6bf1765,0xacca6da1e0a8ef29,
0xbff610b0cc6edd3f,0x17fd090a58d32af3,
0xeff394dcff8a948e,0xddfc4b4cef07f5b0,
0x95f83d0a1fb69cd9,0x4abdaf101564f98e,
0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1,
0xea53df5fd18d5513,0x84c86189216dc5ed,
0x92746b9be2f8552c,0x32fd3cf5b4e49bb4,
0xb7118682dbb66a77,0x3fbc8c33221dc2a1,
0xe4d5e82392a40515,0xfabaf3feaa5334a,
0x8f05b1163ba6832d,0x29cb4d87f2a7400e,
0xb2c71d5bca9023f8,0x743e20e9ef511012,
0xdf78e4b2bd342cf6,0x914da9246b255416,
0x8bab8eefb6409c1a,0x1ad089b6c2f7548e,
0xae9672aba3d0c320,0xa184ac2473b529b1,
0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e,
0x8865899617fb1871,0x7e2fa67c7a658892,
0xaa7eebfb9df9de8d,0xddbb901b98feeab7,
0xd51ea6fa85785631,0x552a74227f3ea565,
0x8533285c936b35de,0xd53a88958f87275f,
0xa67ff273b8460356,0x8a892abaf368f137,
0xd01fef10a657842c,0x2d2b7569b0432d85,
0x8213f56a67f6b29b,0x9c3b29620e29fc73,
0xa298f2c501f45f42,0x8349f3ba91b47b8f,
0xcb3f2f7642717713,0x241c70a936219a73,
0xfe0efb53d30dd4d7,0xed238cd383aa0110,
0x9ec95d1463e8a506,0xf4363804324a40aa,
0xc67bb4597ce2ce48,0xb143c6053edcd0d5,
0xf81aa16fdc1b81da,0xdd94b7868e94050a,
0x9b10a4e5e9913128,0xca7cf2b4191c8326,
0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0,
0xf24a01a73cf2dccf,0xbc633b39673c8cec,
0x976e41088617ca01,0xd5be0503e085d813,
0xbd49d14aa79dbc82,0x4b2d8644d8a74e18,
0xec9c459d51852ba2,0xddf8e7d60ed1219e,
0x93e1ab8252f33b45,0xcabb90e5c942b503,
0xb8da1662e7b00a17,0x3d6a751f3b936243,
0xe7109bfba19c0c9d,0xcc512670a783ad4,
0x906a617d450187e2,0x27fb2b80668b24c5,
0xb484f9dc9641e9da,0xb1f9f660802dedf6,
0xe1a63853bbd26451,0x5e7873f8a0396973,
0x8d07e33455637eb2,0xdb0b487b6423e1e8,
0xb049dc016abc5e5f,0x91ce1a9a3d2cda62,
0xdc5c5301c56b75f7,0x7641a140cc7810fb,
0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d,
0xac2820d9623bf429,0x546345fa9fbdcd44,
0xd732290fbacaf133,0xa97c177947ad4095,
0x867f59a9d4bed6c0,0x49ed8eabcccc485d,
0xa81f301449ee8c70,0x5c68f256bfff5a74,
0xd226fc195c6a2f8c,0x73832eec6fff3111,
0x83585d8fd9c25db7,0xc831fd53c5ff7eab,
0xa42e74f3d032f525,0xba3e7ca8b77f5e55,
0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb,
0x80444b5e7aa7cf85,0x7980d163cf5b81b3,
0xa0555e361951c366,0xd7e105bcc332621f,
0xc86ab5c39fa63440,0x8dd9472bf3fefaa7,
0xfa856334878fc150,0xb14f98f6f0feb951,
0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3,
0xc3b8358109e84f07,0xa862f80ec4700c8,
0xf4a642e14c6262c8,0xcd27bb612758c0fa,
0x98e7e9cccfbd7dbd,0x8038d51cb897789c,
0xbf21e44003acdd2c,0xe0470a63e6bd56c3,
0xeeea5d5004981478,0x1858ccfce06cac74,
0x95527a5202df0ccb,0xf37801e0c43ebc8,
0xbaa718e68396cffd,0xd30560258f54e6ba,
0xe950df20247c83fd,0x47c6b82ef32a2069,
0x91d28b7416cdd27e,0x4cdc331d57fa5441,
0xb6472e511c81471d,0xe0133fe4adf8e952,
0xe3d8f9e563a198e5,0x58180fddd97723a6,
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
};
#endif // SIMDJSON_STATIC_REFLECTION
extern SIMDJSON_DLLIMPORTEXPORT const uint64_t power_of_five_128[651*2];
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H
/* end file simdjson/internal/numberparsing_tables.h */
/* including simdjson/internal/simdprune_tables.h: #include "simdjson/internal/simdprune_tables.h" */
/* begin file simdjson/internal/simdprune_tables.h */
#ifndef SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H
#define SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H
/* skipped duplicate #include "simdjson/base.h" */
#include <cstdint>
namespace simdjson { // table modified and copied from
namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable
extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256];
extern SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272];
// 256 * 8 bytes = 2kB, easily fits in cache.
extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256];
} // namespace internal
} // namespace simdjson
#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H
/* end file simdjson/internal/simdprune_tables.h */
#endif // SIMDJSON_GENERIC_DEPENDENCIES_H
/* end file simdjson/generic/dependencies.h */
/* defining SIMDJSON_CONDITIONAL_INCLUDE */
#define SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64)
/* including simdjson/arm64/implementation.h: #include "simdjson/arm64/implementation.h" */
/* begin file simdjson/arm64/implementation.h */
#ifndef SIMDJSON_ARM64_IMPLEMENTATION_H
#define SIMDJSON_ARM64_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation() : simdjson::implementation("arm64", "ARM NEON", internal::instruction_set::NEON) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity,
size_t max_length,
std::unique_ptr<internal::dom_parser_implementation>& dst
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
};
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_IMPLEMENTATION_H
/* end file simdjson/arm64/implementation.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback)
/* including simdjson/fallback/implementation.h: #include "simdjson/fallback/implementation.h" */
/* begin file simdjson/fallback/implementation.h */
#ifndef SIMDJSON_FALLBACK_IMPLEMENTATION_H
#define SIMDJSON_FALLBACK_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation() : simdjson::implementation(
"fallback",
"Generic fallback implementation",
0
) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity,
size_t max_length,
std::unique_ptr<simdjson::internal::dom_parser_implementation>& dst
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
};
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H
/* end file simdjson/fallback/implementation.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell)
/* including simdjson/haswell/implementation.h: #include "simdjson/haswell/implementation.h" */
/* begin file simdjson/haswell/implementation.h */
#ifndef SIMDJSON_HASWELL_IMPLEMENTATION_H
#define SIMDJSON_HASWELL_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL
namespace simdjson {
namespace haswell {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation() : simdjson::implementation(
"haswell",
"Intel/AMD AVX2",
internal::instruction_set::AVX2 | internal::instruction_set::PCLMULQDQ | internal::instruction_set::BMI1 | internal::instruction_set::BMI2
) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity,
size_t max_length,
std::unique_ptr<internal::dom_parser_implementation>& dst
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
};
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_IMPLEMENTATION_H
/* end file simdjson/haswell/implementation.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake)
/* including simdjson/icelake/implementation.h: #include "simdjson/icelake/implementation.h" */
/* begin file simdjson/icelake/implementation.h */
#ifndef SIMDJSON_ICELAKE_IMPLEMENTATION_H
#define SIMDJSON_ICELAKE_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE
namespace simdjson {
namespace icelake {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation() : simdjson::implementation(
"icelake",
"Intel/AMD AVX512",
internal::instruction_set::AVX2 | internal::instruction_set::PCLMULQDQ | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 | internal::instruction_set::AVX512F | internal::instruction_set::AVX512DQ | internal::instruction_set::AVX512CD | internal::instruction_set::AVX512BW | internal::instruction_set::AVX512VL | internal::instruction_set::AVX512VBMI2
) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity,
size_t max_length,
std::unique_ptr<internal::dom_parser_implementation>& dst
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
};
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H
/* end file simdjson/icelake/implementation.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64)
/* including simdjson/ppc64/implementation.h: #include "simdjson/ppc64/implementation.h" */
/* begin file simdjson/ppc64/implementation.h */
#ifndef SIMDJSON_PPC64_IMPLEMENTATION_H
#define SIMDJSON_PPC64_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for ALTIVEC (PPC64).
*/
namespace ppc64 {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation()
: simdjson::implementation("ppc64", "PPC64 ALTIVEC",
internal::instruction_set::ALTIVEC) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity, size_t max_length,
std::unique_ptr<internal::dom_parser_implementation> &dst)
const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len,
uint8_t *dst,
size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf,
size_t len) const noexcept final;
};
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_IMPLEMENTATION_H
/* end file simdjson/ppc64/implementation.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
/* including simdjson/westmere/implementation.h: #include "simdjson/westmere/implementation.h" */
/* begin file simdjson/westmere/implementation.h */
#ifndef SIMDJSON_WESTMERE_IMPLEMENTATION_H
#define SIMDJSON_WESTMERE_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE
namespace simdjson {
namespace westmere {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation() : simdjson::implementation("westmere", "Intel/AMD SSE4.2", internal::instruction_set::SSE42 | internal::instruction_set::PCLMULQDQ) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity,
size_t max_length,
std::unique_ptr<internal::dom_parser_implementation>& dst
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
};
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H
/* end file simdjson/westmere/implementation.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
/* including simdjson/lsx/implementation.h: #include "simdjson/lsx/implementation.h" */
/* begin file simdjson/lsx/implementation.h */
#ifndef SIMDJSON_LSX_IMPLEMENTATION_H
#define SIMDJSON_LSX_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation() : simdjson::implementation("lsx", "LoongArch SX", internal::instruction_set::LSX) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity,
size_t max_length,
std::unique_ptr<internal::dom_parser_implementation>& dst
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
};
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_IMPLEMENTATION_H
/* end file simdjson/lsx/implementation.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
/* including simdjson/lasx/implementation.h: #include "simdjson/lasx/implementation.h" */
/* begin file simdjson/lasx/implementation.h */
#ifndef SIMDJSON_LASX_IMPLEMENTATION_H
#define SIMDJSON_LASX_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/instruction_set.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation() : simdjson::implementation("lasx", "LoongArch ASX", internal::instruction_set::LASX) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity,
size_t max_length,
std::unique_ptr<internal::dom_parser_implementation>& dst
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
};
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_IMPLEMENTATION_H
/* end file simdjson/lasx/implementation.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
/* including simdjson/rvv-vls/implementation.h: #include "simdjson/rvv-vls/implementation.h" */
/* begin file simdjson/rvv-vls/implementation.h */
#ifndef SIMDJSON_RVV_VLS_IMPLEMENTATION_H
#define SIMDJSON_RVV_VLS_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
/**
* @private
*/
class implementation final : public simdjson::implementation {
public:
simdjson_inline implementation() : simdjson::implementation(
"rvv_vls",
"RISC-V V extension",
internal::instruction_set::RVV_VLS
) {}
simdjson_warn_unused error_code create_dom_parser_implementation(
size_t capacity,
size_t max_length,
std::unique_ptr<simdjson::internal::dom_parser_implementation>& dst
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
};
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_IMPLEMENTATION_H
/* end file simdjson/rvv-vls/implementation.h */
#else
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
#endif
/* undefining SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
/**
* Function which returns a pointer to an implementation matching the "builtin" implementation.
* The builtin implementation is the best statically linked simdjson implementation that can be used by the compiling
* program. If you compile with g++ -march=haswell, this will return the haswell implementation.
* It is handy to be able to check what builtin was used: builtin_implementation()->name().
*/
const implementation * builtin_implementation();
} // namespace simdjson
#endif // SIMDJSON_BUILTIN_IMPLEMENTATION_H
/* end file simdjson/builtin/implementation.h */
/* skipped duplicate #include "simdjson/generic/dependencies.h" */
/* defining SIMDJSON_CONDITIONAL_INCLUDE */
#define SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64)
/* including simdjson/arm64.h: #include "simdjson/arm64.h" */
/* begin file simdjson/arm64.h */
#ifndef SIMDJSON_ARM64_H
#define SIMDJSON_ARM64_H
/* including simdjson/arm64/begin.h: #include "simdjson/arm64/begin.h" */
/* begin file simdjson/arm64/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "arm64" */
#define SIMDJSON_IMPLEMENTATION arm64
/* including simdjson/arm64/base.h: #include "simdjson/arm64/base.h" */
/* begin file simdjson/arm64/base.h */
#ifndef SIMDJSON_ARM64_BASE_H
#define SIMDJSON_ARM64_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for NEON (ARMv8).
*/
namespace arm64 {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_BASE_H
/* end file simdjson/arm64/base.h */
/* including simdjson/arm64/intrinsics.h: #include "simdjson/arm64/intrinsics.h" */
/* begin file simdjson/arm64/intrinsics.h */
#ifndef SIMDJSON_ARM64_INTRINSICS_H
#define SIMDJSON_ARM64_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// This should be the correct header whether
// you use visual studio or other compilers.
#include <arm_neon.h>
static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient padding for arm64");
#endif // SIMDJSON_ARM64_INTRINSICS_H
/* end file simdjson/arm64/intrinsics.h */
/* including simdjson/arm64/bitmanipulation.h: #include "simdjson/arm64/bitmanipulation.h" */
/* begin file simdjson/arm64/bitmanipulation.h */
#ifndef SIMDJSON_ARM64_BITMANIPULATION_H
#define SIMDJSON_ARM64_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
// We sometimes call leading_zeroes on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true.
// (See below.)
SIMDJSON_NO_SANITIZE_UNDEFINED
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return vaddv_u8(vcnt_u8(vcreate_u8(input_num)));
#else
// if the system supports SVE or CSSC, __builtin_popcountll
// might be compiled to fewer single instructions. For CSSC,
// __builtin_popcountll is compiled to a single instruction.
return __builtin_popcountll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if defined(__GNUC__) // catches clang and gcc
/**
* ARM has a fast 64-bit "bit reversal function" that is handy. However,
* it is not generally available as an intrinsic function under Visual
* Studio (though this might be changing). Even under clang/gcc, we
* apparently need to invoke inline assembly.
*/
/*
* We use SIMDJSON_PREFER_REVERSE_BITS as a hint that algorithms that
* work well with bit reversal may use it.
*/
#define SIMDJSON_PREFER_REVERSE_BITS 1
/* reverse the bits */
simdjson_inline uint64_t reverse_bits(uint64_t input_num) {
uint64_t rev_bits;
__asm("rbit %0, %1" : "=r"(rev_bits) : "r"(input_num));
return rev_bits;
}
/**
* Flips bit at index 63 - lz. Thus if you have 'leading_zeroes' leading zeroes,
* then this will set to zero the leading bit. It is possible for leading_zeroes to be
* greating or equal to 63 in which case we trigger undefined behavior, but the output
* of such undefined behavior is never used.
**/
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) {
return rev_bits ^ (uint64_t(0x8000000000000000) >> leading_zeroes);
}
#endif
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_BITMANIPULATION_H
/* end file simdjson/arm64/bitmanipulation.h */
/* including simdjson/arm64/bitmask.h: #include "simdjson/arm64/bitmask.h" */
/* begin file simdjson/arm64/bitmask.h */
#ifndef SIMDJSON_ARM64_BITMASK_H
#define SIMDJSON_ARM64_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
/////////////
// We could do this with PMULL, but it is apparently slow.
//
//#ifdef __ARM_FEATURE_CRYPTO // some ARM processors lack this extension
//return vmull_p64(-1ULL, bitmask);
//#else
// Analysis by @sebpop:
// When diffing the assembly for src/stage1_find_marks.cpp I see that the eors are all spread out
// in between other vector code, so effectively the extra cycles of the sequence do not matter
// because the GPR units are idle otherwise and the critical path is on the FP side.
// Also the PMULL requires two extra fmovs: GPR->FP (3 cycles in N1, 5 cycles in A72 )
// and FP->GPR (2 cycles on N1 and 5 cycles on A72.)
///////////
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif
/* end file simdjson/arm64/bitmask.h */
/* including simdjson/arm64/numberparsing_defs.h: #include "simdjson/arm64/numberparsing_defs.h" */
/* begin file simdjson/arm64/numberparsing_defs.h */
#ifndef SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
#define SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#if SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
// __umulh requires intrin.h
#include <intrin.h>
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
namespace simdjson {
namespace arm64 {
namespace numberparsing {
// we don't have SSE, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace arm64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
/* end file simdjson/arm64/numberparsing_defs.h */
/* including simdjson/arm64/simd.h: #include "simdjson/arm64/simd.h" */
/* begin file simdjson/arm64/simd.h */
#ifndef SIMDJSON_ARM64_SIMD_H
#define SIMDJSON_ARM64_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
namespace simd {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
namespace {
// Start of private section with Visual Studio workaround
#ifndef simdjson_make_uint8x16_t
#define simdjson_make_uint8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
x13, x14, x15, x16) \
([=]() { \
uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
x9, x10, x11, x12, x13, x14, x15, x16}; \
return vld1q_u8(array); \
}())
#endif
#ifndef simdjson_make_int8x16_t
#define simdjson_make_int8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
x13, x14, x15, x16) \
([=]() { \
int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
x9, x10, x11, x12, x13, x14, x15, x16}; \
return vld1q_s8(array); \
}())
#endif
#ifndef simdjson_make_uint8x8_t
#define simdjson_make_uint8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
uint8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1_u8(array); \
}())
#endif
#ifndef simdjson_make_int8x8_t
#define simdjson_make_int8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
int8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1_s8(array); \
}())
#endif
#ifndef simdjson_make_uint16x8_t
#define simdjson_make_uint16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
uint16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1q_u16(array); \
}())
#endif
#ifndef simdjson_make_int16x8_t
#define simdjson_make_int16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
int16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1q_s16(array); \
}())
#endif
// End of private section with Visual Studio workaround
} // namespace
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
template<typename T>
struct simd8;
//
// Base class of simd8<uint8_t> and simd8<bool>, both of which use uint8x16_t internally.
//
template<typename T, typename Mask=simd8<bool>>
struct base_u8 {
uint8x16_t value;
static const int SIZE = sizeof(value);
// Conversion from/to SIMD register
simdjson_inline base_u8(const uint8x16_t _value) : value(_value) {}
simdjson_inline operator const uint8x16_t&() const { return this->value; }
simdjson_inline operator uint8x16_t&() { return this->value; }
// Bit operations
simdjson_inline simd8<T> operator|(const simd8<T> other) const { return vorrq_u8(*this, other); }
simdjson_inline simd8<T> operator&(const simd8<T> other) const { return vandq_u8(*this, other); }
simdjson_inline simd8<T> operator^(const simd8<T> other) const { return veorq_u8(*this, other); }
simdjson_inline simd8<T> bit_andnot(const simd8<T> other) const { return vbicq_u8(*this, other); }
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
simdjson_inline simd8<T>& operator|=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline simd8<T>& operator&=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline simd8<T>& operator^=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return vceqq_u8(lhs, rhs); }
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return vextq_u8(prev_chunk, *this, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base_u8<bool> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
static simdjson_inline simd8<bool> splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); }
simdjson_inline simd8(const uint8x16_t _value) : base_u8<bool>(_value) {}
// False constructor
simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {}
// Splat constructor
simdjson_inline simd8(bool _value) : simd8(splat(_value)) {}
// We return uint32_t instead of uint16_t because that seems to be more efficient for most
// purposes (cutting it down to uint16_t costs performance in some compilers).
simdjson_inline uint32_t to_bitmask() const {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
const uint8x16_t bit_mask = simdjson_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
#else
const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
#endif
auto minput = *this & bit_mask;
uint8x16_t tmp = vpaddq_u8(minput, minput);
tmp = vpaddq_u8(tmp, tmp);
tmp = vpaddq_u8(tmp, tmp);
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
// Integer umaxv, not a float compare: flush-to-zero would treat a denormal as zero.
simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base_u8<uint8_t> {
static simdjson_inline uint8x16_t splat(uint8_t _value) { return vmovq_n_u8(_value); }
static simdjson_inline uint8x16_t zero() { return vdupq_n_u8(0); }
static simdjson_inline uint8x16_t load(const uint8_t* values) { return vld1q_u8(values); }
simdjson_inline simd8(const uint8x16_t _value) : base_u8<uint8_t>(_value) {}
// Zero constructor
simdjson_inline simd8() : simd8(zero()) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[16]) : simd8(load(values)) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Member-by-member initialization
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(simdjson_make_uint8x16_t(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
#else
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(uint8x16_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
#endif
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Store to array
simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); }
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return vqaddq_u8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return vqsubq_u8(*this, other); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<uint8_t> operator+(const simd8<uint8_t> other) const { return vaddq_u8(*this, other); }
simdjson_inline simd8<uint8_t> operator-(const simd8<uint8_t> other) const { return vsubq_u8(*this, other); }
simdjson_inline simd8<uint8_t>& operator+=(const simd8<uint8_t> other) { *this = *this + other; return *this; }
simdjson_inline simd8<uint8_t>& operator-=(const simd8<uint8_t> other) { *this = *this - other; return *this; }
// Order-specific operations
simdjson_inline uint8_t max_val() const { return vmaxvq_u8(*this); }
simdjson_inline uint8_t min_val() const { return vminvq_u8(*this); }
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return vmaxq_u8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return vminq_u8(*this, other); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return vcleq_u8(*this, other); }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return vcgeq_u8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return vcltq_u8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return vcgtq_u8(*this, other); }
// Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's.
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this > other); }
// Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's.
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this < other); }
// Bit-specific operations
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return vtstq_u8(*this, bits); }
simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return vshrq_n_u8(*this, N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return vshlq_n_u8(*this, N); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint16_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]};
uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64);
// we increment by 0x08 the second half of the mask
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint8x16_t inc = simdjson_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
#else
uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
#endif
shufmask = vaddq_u8(shufmask, inc);
// this is the version "nearly pruned"
uint8x16_t pruned = vqtbl1q_u8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
uint8x16_t compactmask = vld1q_u8(reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
uint8x16_t answer = vqtbl1q_u8(pruned, compactmask);
vst1q_u8(reinterpret_cast<uint8_t*>(output), answer);
}
// Copies all bytes corresponding to a 0 in the low half of the mask (interpreted as a
// bitset) to output1, then those corresponding to a 0 in the high half to output2.
template<typename L>
simdjson_inline void compress_halves(uint16_t mask, L *output1, L *output2) const {
using internal::thintable_epi8;
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
uint8x8_t compactmask1 = vcreate_u8(thintable_epi8[mask1]);
uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]);
// we increment by 0x08 the second half of the mask
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
#else
uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
#endif
compactmask2 = vadd_u8(compactmask2, inc);
// store each result (with the second store possibly overlapping the first)
vst1_u8((uint8_t*)output1, vqtbl1_u8(*this, compactmask1));
vst1_u8((uint8_t*)output2, vqtbl1_u8(*this, compactmask2));
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
template<typename T>
simdjson_inline simd8<uint8_t> apply_lookup_16_to(const simd8<T> original) {
return vqtbl1q_u8(*this, simd8<uint8_t>(original));
}
};
// Signed bytes
template<>
struct simd8<int8_t> {
int8x16_t value;
static simdjson_inline simd8<int8_t> splat(int8_t _value) { return vmovq_n_s8(_value); }
static simdjson_inline simd8<int8_t> zero() { return vdupq_n_s8(0); }
static simdjson_inline simd8<int8_t> load(const int8_t values[16]) { return vld1q_s8(values); }
// Conversion from/to SIMD register
simdjson_inline simd8(const int8x16_t _value) : value{_value} {}
simdjson_inline operator const int8x16_t&() const { return this->value; }
simdjson_inline operator int8x16_t&() { return this->value; }
// Zero constructor
simdjson_inline simd8() : simd8(zero()) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(simdjson_make_int8x16_t(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
#else
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(int8x16_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
#endif
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Store to array
simdjson_inline void store(int8_t dst[16]) const { return vst1q_s8(dst, *this); }
// Explicit conversion to/from unsigned
//
// Under Visual Studio/ARM64 uint8x16_t and int8x16_t are apparently the same type.
// In theory, we could check this occurrence with std::same_as and std::enabled_if but it is C++14
// and relatively ugly and hard to read.
#ifndef SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {}
#endif
simdjson_inline explicit operator simd8<uint8_t>() const { return vreinterpretq_u8_s8(this->value); }
// Math
simdjson_inline simd8<int8_t> operator+(const simd8<int8_t> other) const { return vaddq_s8(*this, other); }
simdjson_inline simd8<int8_t> operator-(const simd8<int8_t> other) const { return vsubq_s8(*this, other); }
simdjson_inline simd8<int8_t>& operator+=(const simd8<int8_t> other) { *this = *this + other; return *this; }
simdjson_inline simd8<int8_t>& operator-=(const simd8<int8_t> other) { *this = *this - other; return *this; }
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return vmaxq_s8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return vminq_s8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return vcgtq_s8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return vcltq_s8(*this, other); }
simdjson_inline simd8<bool> operator==(const simd8<int8_t> other) const { return vceqq_s8(*this, other); }
template<int N=1>
simdjson_inline simd8<int8_t> prev(const simd8<int8_t> prev_chunk) const {
return vextq_s8(prev_chunk, *this, 16 - N);
}
// Perform a lookup assuming no value is larger than 16
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
template<typename T>
simdjson_inline simd8<int8_t> apply_lookup_16_to(const simd8<T> original) {
return vqtbl1q_s8(*this, simd8<uint8_t>(original));
}
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint64_t popcounts = vget_lane_u64(vreinterpret_u64_u8(vcnt_u8(vcreate_u8(~mask))), 0);
// compute the prefix sum of the popcounts of each byte
uint64_t offsets = popcounts * 0x0101010101010101;
this->chunks[0].compress_halves(uint16_t(mask), output, &output[popcounts & 0xFF]);
this->chunks[1].compress_halves(uint16_t(mask >> 16), &output[(offsets >> 8) & 0xFF], &output[(offsets >> 16) & 0xFF]);
this->chunks[2].compress_halves(uint16_t(mask >> 32), &output[(offsets >> 24) & 0xFF], &output[(offsets >> 32) & 0xFF]);
this->chunks[3].compress_halves(uint16_t(mask >> 48), &output[(offsets >> 40) & 0xFF], &output[(offsets >> 48) & 0xFF]);
return offsets >> 56;
}
simdjson_inline uint64_t to_bitmask() const {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
const uint8x16_t bit_mask = simdjson_make_uint8x16_t(
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
);
#else
const uint8x16_t bit_mask = {
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
};
#endif
// Add each of the elements next to each other, successively, to stuff each 8 byte mask into one.
uint8x16_t sum0 = vpaddq_u8(this->chunks[0] & bit_mask, this->chunks[1] & bit_mask);
uint8x16_t sum1 = vpaddq_u8(this->chunks[2] & bit_mask, this->chunks[3] & bit_mask);
sum0 = vpaddq_u8(sum0, sum1);
sum0 = vpaddq_u8(sum0, sum0);
return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_SIMD_H
/* end file simdjson/arm64/simd.h */
/* including simdjson/arm64/stringparsing_defs.h: #include "simdjson/arm64/stringparsing_defs.h" */
/* begin file simdjson/arm64/stringparsing_defs.h */
#ifndef SIMDJSON_ARM64_STRINGPARSING_DEFS_H
#define SIMDJSON_ARM64_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on ARM; therefore, we
// smash them together into a 64-byte mask and get the bitmask from there.
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask64()
};
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_STRINGPARSING_DEFS_H
/* end file simdjson/arm64/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/arm64/begin.h */
/* including simdjson/generic/amalgamated.h for arm64: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for arm64 */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for arm64: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for arm64 */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for arm64 */
/* including simdjson/generic/jsoncharutils.h for arm64: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for arm64 */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for arm64 */
/* including simdjson/generic/atomparsing.h for arm64: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for arm64 */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace arm64 {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for arm64 */
/* including simdjson/generic/dom_parser_implementation.h for arm64: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for arm64 */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace arm64
} // namespace simdjson
namespace simdjson {
namespace arm64 {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for arm64 */
/* including simdjson/generic/implementation_simdjson_result_base.h for arm64: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for arm64 */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for arm64 */
/* including simdjson/generic/numberparsing.h for arm64: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for arm64 */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace arm64 {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for arm64 */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for arm64: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for arm64 */
/* end file simdjson/generic/amalgamated.h for arm64 */
/* including simdjson/arm64/end.h: #include "simdjson/arm64/end.h" */
/* begin file simdjson/arm64/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "arm64" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/arm64/end.h */
#endif // SIMDJSON_ARM64_H
/* end file simdjson/arm64.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback)
/* including simdjson/fallback.h: #include "simdjson/fallback.h" */
/* begin file simdjson/fallback.h */
#ifndef SIMDJSON_FALLBACK_H
#define SIMDJSON_FALLBACK_H
/* including simdjson/fallback/begin.h: #include "simdjson/fallback/begin.h" */
/* begin file simdjson/fallback/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "fallback" */
#define SIMDJSON_IMPLEMENTATION fallback
/* including simdjson/fallback/base.h: #include "simdjson/fallback/base.h" */
/* begin file simdjson/fallback/base.h */
#ifndef SIMDJSON_FALLBACK_BASE_H
#define SIMDJSON_FALLBACK_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Fallback implementation (runs on any machine).
*/
namespace fallback {
class implementation;
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_BASE_H
/* end file simdjson/fallback/base.h */
/* including simdjson/fallback/bitmanipulation.h: #include "simdjson/fallback/bitmanipulation.h" */
/* begin file simdjson/fallback/bitmanipulation.h */
#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H
#define SIMDJSON_FALLBACK_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace {
#if defined(_MSC_VER) && !defined(_M_ARM64) && !defined(_M_X64)
static inline unsigned char _BitScanForward64(unsigned long* ret, uint64_t x) {
unsigned long x0 = (unsigned long)x, top, bottom;
_BitScanForward(&top, (unsigned long)(x >> 32));
_BitScanForward(&bottom, x0);
*ret = x0 ? bottom : 32 + top;
return x != 0;
}
static unsigned char _BitScanReverse64(unsigned long* ret, uint64_t x) {
unsigned long x1 = (unsigned long)(x >> 32), top, bottom;
_BitScanReverse(&top, x1);
_BitScanReverse(&bottom, (unsigned long)x);
*ret = x1 ? top + 32 : bottom;
return x != 0;
}
#endif
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#ifdef _MSC_VER
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// _MSC_VER
}
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#ifdef _MSC_VER
unsigned long trailing_zero = 0;
// Search the mask data from least significant bit (LSB)
// to most significant bit (MSB) for a set bit (1).
if (_BitScanForward64(&trailing_zero, input_num))
return (int)trailing_zero;
else return 64;
#else
return __builtin_ctzll(input_num);
#endif// _MSC_VER
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_BITMANIPULATION_H
/* end file simdjson/fallback/bitmanipulation.h */
/* including simdjson/fallback/stringparsing_defs.h: #include "simdjson/fallback/stringparsing_defs.h" */
/* begin file simdjson/fallback/stringparsing_defs.h */
#ifndef SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
#define SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace {
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return c == '"'; }
simdjson_inline bool has_backslash() { return c == '\\'; }
simdjson_inline int quote_index() { return c == '"' ? 0 : 1; }
simdjson_inline int backslash_index() { return c == '\\' ? 0 : 1; }
uint8_t c;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// store to dest unconditionally - we can overwrite the bits we don't like later
dst[0] = src[0];
return { src[0] };
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits; }
simdjson_inline int escape_index() { return 0; }
bool escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
dst[0] = src[0];
return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
/* end file simdjson/fallback/stringparsing_defs.h */
/* including simdjson/fallback/numberparsing_defs.h: #include "simdjson/fallback/numberparsing_defs.h" */
/* begin file simdjson/fallback/numberparsing_defs.h */
#ifndef SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
#define SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#ifdef JSON_TEST_NUMBERS // for unit testing
void found_invalid_number(const uint8_t *buf);
void found_integer(int64_t result, const uint8_t *buf);
void found_unsigned_integer(uint64_t result, const uint8_t *buf);
void found_float(double result, const uint8_t *buf);
#endif
namespace simdjson {
namespace fallback {
namespace numberparsing {
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) {
uint64_t val;
memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars));
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace fallback
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
/* end file simdjson/fallback/numberparsing_defs.h */
/* end file simdjson/fallback/begin.h */
/* including simdjson/generic/amalgamated.h for fallback: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for fallback */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for fallback: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for fallback */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for fallback */
/* including simdjson/generic/jsoncharutils.h for fallback: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for fallback */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for fallback */
/* including simdjson/generic/atomparsing.h for fallback: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for fallback */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace fallback {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for fallback */
/* including simdjson/generic/dom_parser_implementation.h for fallback: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for fallback */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace fallback
} // namespace simdjson
namespace simdjson {
namespace fallback {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for fallback */
/* including simdjson/generic/implementation_simdjson_result_base.h for fallback: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for fallback */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for fallback */
/* including simdjson/generic/numberparsing.h for fallback: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for fallback */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace fallback {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for fallback */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for fallback: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for fallback */
/* end file simdjson/generic/amalgamated.h for fallback */
/* including simdjson/fallback/end.h: #include "simdjson/fallback/end.h" */
/* begin file simdjson/fallback/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/* undefining SIMDJSON_IMPLEMENTATION from "fallback" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/fallback/end.h */
#endif // SIMDJSON_FALLBACK_H
/* end file simdjson/fallback.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell)
/* including simdjson/haswell.h: #include "simdjson/haswell.h" */
/* begin file simdjson/haswell.h */
#ifndef SIMDJSON_HASWELL_H
#define SIMDJSON_HASWELL_H
/* including simdjson/haswell/begin.h: #include "simdjson/haswell/begin.h" */
/* begin file simdjson/haswell/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "haswell" */
#define SIMDJSON_IMPLEMENTATION haswell
/* including simdjson/haswell/base.h: #include "simdjson/haswell/base.h" */
/* begin file simdjson/haswell/base.h */
#ifndef SIMDJSON_HASWELL_BASE_H
#define SIMDJSON_HASWELL_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL
namespace simdjson {
/**
* Implementation for Haswell (Intel AVX2).
*/
namespace haswell {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BASE_H
/* end file simdjson/haswell/base.h */
/* including simdjson/haswell/intrinsics.h: #include "simdjson/haswell/intrinsics.h" */
/* begin file simdjson/haswell/intrinsics.h */
#ifndef SIMDJSON_HASWELL_INTRINSICS_H
#define SIMDJSON_HASWELL_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
* e.g., if __AVX2__ is set... in turn, we normally set these
* macros by compiling against the corresponding architecture
* (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
* software with these advanced instructions. In simdjson, we
* want to compile the whole program for a generic target,
* and only target our specific kernels. As a workaround,
* we directly include the needed headers. These headers would
* normally guard against such usage, but we carefully included
* <x86intrin.h> (or <intrin.h>) before, so the headers
* are fooled.
*/
#include <bmiintrin.h> // for _blsr_u64
#include <lzcntintrin.h> // for __lzcnt64
#include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
#include <smmintrin.h>
#include <tmmintrin.h>
#include <avxintrin.h>
#include <avx2intrin.h>
#include <wmmintrin.h> // for _mm_clmulepi64_si128
// unfortunately, we may not get _blsr_u64, but, thankfully, clang
// has it as a macro.
#ifndef _blsr_u64
// we roll our own
#define _blsr_u64(n) ((n - 1) & n)
#endif // _blsr_u64
#endif // SIMDJSON_CLANG_VISUAL_STUDIO
static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for haswell kernel.");
#endif // SIMDJSON_HASWELL_INTRINSICS_H
/* end file simdjson/haswell/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
// We enable bmi2 only if LLVM/clang is used, because GCC may not
// make good use of it. See https://github.com/simdjson/simdjson/pull/2243
#if defined(__clang__)
SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt")
#else
SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt")
#endif
#endif
/* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */
/* begin file simdjson/haswell/bitmanipulation.h */
#ifndef SIMDJSON_HASWELL_BITMANIPULATION_H
#define SIMDJSON_HASWELL_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BITMANIPULATION_H
/* end file simdjson/haswell/bitmanipulation.h */
/* including simdjson/haswell/bitmask.h: #include "simdjson/haswell/bitmask.h" */
/* begin file simdjson/haswell/bitmask.h */
#ifndef SIMDJSON_HASWELL_BITMASK_H
#define SIMDJSON_HASWELL_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processor supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BITMASK_H
/* end file simdjson/haswell/bitmask.h */
/* including simdjson/haswell/numberparsing_defs.h: #include "simdjson/haswell/numberparsing_defs.h" */
/* begin file simdjson/haswell/numberparsing_defs.h */
#ifndef SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
#define SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace numberparsing {
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace haswell
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
/* end file simdjson/haswell/numberparsing_defs.h */
/* including simdjson/haswell/simd.h: #include "simdjson/haswell/simd.h" */
/* begin file simdjson/haswell/simd.h */
#ifndef SIMDJSON_HASWELL_SIMD_H
#define SIMDJSON_HASWELL_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m256i value;
// Zero constructor
simdjson_inline base() : value{__m256i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m256i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m256i&() const { return this->value; }
simdjson_inline operator __m256i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm256_or_si256(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm256_and_si256(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm256_xor_si256(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm256_andnot_si256(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint32_t bitmask_t;
typedef uint64_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm256_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm256_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm256_testz_si256(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm256_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm256_setzero_si256(); }
static simdjson_inline simd8<T> load(const T values[32]) {
return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm256_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm256_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm256_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint32_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in four steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second least significant 8 bits
uint8_t mask3 = uint8_t(mask >> 16); // ...
uint8_t mask4 = uint8_t(mask >> 24); // ...
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m256i shufmask = _mm256_set_epi64x(thintable_epi8[mask4], thintable_epi8[mask3],
thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask and so forth
shufmask =
_mm256_add_epi8(shufmask, _mm256_set_epi32(0x18181818, 0x18181818,
0x10101010, 0x10101010, 0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m256i pruned = _mm256_shuffle_epi8(*this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
int pop3 = BitsSetTable256mul2[mask3];
// then load the corresponding mask
// could be done with _mm256_loadu2_m128i but many standard libraries omit this intrinsic.
__m256i v256 = _mm256_castsi128_si256(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8)));
__m256i compactmask = _mm256_insertf128_si256(v256,
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop3 * 8)), 1);
__m256i almostthere = _mm256_shuffle_epi8(pruned, compactmask);
// We just need to write out the result.
// This is the tricky bit that is hard to do
// if we want to return a SIMD register, since there
// is no single-instruction approach to recombine
// the two 128-bit lanes with an offset.
__m128i v128;
v128 = _mm256_castsi256_si128(almostthere);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output), v128);
v128 = _mm256_extractf128_si256(almostthere, 1);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output + 16 - count_ones(mask & 0xFFFF)), v128);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31
) : simd8(_mm256_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm256_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm256_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm256_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm256_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31
) : simd8(_mm256_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm256_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm256_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm256_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm256_min_epu8(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm256_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm256_testz_si256(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm256_testz_si256(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm256_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+32)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint32_t mask1 = uint32_t(mask);
uint32_t mask2 = uint32_t(mask >> 32);
this->chunks[0].compress(mask1, output);
this->chunks[1].compress(mask2, output + 32 - count_ones(mask1));
return 64 - count_ones(mask);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
uint64_t r_hi = this->chunks[1].to_bitmask();
return r_lo | (r_hi << 32);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0] | this->chunks[1];
}
simdjson_inline simd8x64<T> bit_or(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<T>(
this->chunks[0] | mask,
this->chunks[1] | mask
);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_SIMD_H
/* end file simdjson/haswell/simd.h */
/* including simdjson/haswell/stringparsing_defs.h: #include "simdjson/haswell/stringparsing_defs.h" */
/* begin file simdjson/haswell/stringparsing_defs.h */
#ifndef SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
#define SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 15 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
// store to dest unconditionally - we can overwrite the bits we don't like later
v.store(dst);
return {
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
/* end file simdjson/haswell/stringparsing_defs.h */
/* end file simdjson/haswell/begin.h */
/* including simdjson/generic/amalgamated.h for haswell: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for haswell */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for haswell: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for haswell */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for haswell */
/* including simdjson/generic/jsoncharutils.h for haswell: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for haswell */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for haswell */
/* including simdjson/generic/atomparsing.h for haswell: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for haswell */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace haswell {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for haswell */
/* including simdjson/generic/dom_parser_implementation.h for haswell: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for haswell */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace haswell
} // namespace simdjson
namespace simdjson {
namespace haswell {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for haswell */
/* including simdjson/generic/implementation_simdjson_result_base.h for haswell: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for haswell */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for haswell */
/* including simdjson/generic/numberparsing.h for haswell: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for haswell */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace haswell {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for haswell */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for haswell: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for haswell */
/* end file simdjson/generic/amalgamated.h for haswell */
/* including simdjson/haswell/end.h: #include "simdjson/haswell/end.h" */
/* begin file simdjson/haswell/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "haswell" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/haswell/end.h */
#endif // SIMDJSON_HASWELL_H
/* end file simdjson/haswell.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake)
/* including simdjson/icelake.h: #include "simdjson/icelake.h" */
/* begin file simdjson/icelake.h */
#ifndef SIMDJSON_ICELAKE_H
#define SIMDJSON_ICELAKE_H
/* including simdjson/icelake/begin.h: #include "simdjson/icelake/begin.h" */
/* begin file simdjson/icelake/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "icelake" */
#define SIMDJSON_IMPLEMENTATION icelake
/* including simdjson/icelake/base.h: #include "simdjson/icelake/base.h" */
/* begin file simdjson/icelake/base.h */
#ifndef SIMDJSON_ICELAKE_BASE_H
#define SIMDJSON_ICELAKE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE
namespace simdjson {
/**
* Implementation for Icelake (Intel AVX512).
*/
namespace icelake {
class implementation;
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BASE_H
/* end file simdjson/icelake/base.h */
/* including simdjson/icelake/intrinsics.h: #include "simdjson/icelake/intrinsics.h" */
/* begin file simdjson/icelake/intrinsics.h */
#ifndef SIMDJSON_ICELAKE_INTRINSICS_H
#define SIMDJSON_ICELAKE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
* e.g., if __AVX2__ is set... in turn, we normally set these
* macros by compiling against the corresponding architecture
* (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
* software with these advanced instructions. In simdjson, we
* want to compile the whole program for a generic target,
* and only target our specific kernels. As a workaround,
* we directly include the needed headers. These headers would
* normally guard against such usage, but we carefully included
* <x86intrin.h> (or <intrin.h>) before, so the headers
* are fooled.
*/
#include <bmiintrin.h> // for _blsr_u64
#include <lzcntintrin.h> // for __lzcnt64
#include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
#include <smmintrin.h>
#include <tmmintrin.h>
#include <avxintrin.h>
#include <avx2intrin.h>
#include <wmmintrin.h> // for _mm_clmulepi64_si128
// Important: we need the AVX-512 headers:
#include <avx512fintrin.h>
#include <avx512dqintrin.h>
#include <avx512cdintrin.h>
#include <avx512bwintrin.h>
#include <avx512vlintrin.h>
#include <avx512vbmiintrin.h>
#include <avx512vbmi2intrin.h>
// unfortunately, we may not get _blsr_u64, but, thankfully, clang
// has it as a macro.
#ifndef _blsr_u64
// we roll our own
#define _blsr_u64(n) ((n - 1) & n)
#endif // _blsr_u64
#endif // SIMDJSON_CLANG_VISUAL_STUDIO
static_assert(sizeof(__m512i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for icelake");
#endif // SIMDJSON_ICELAKE_INTRINSICS_H
/* end file simdjson/icelake/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_ICELAKE
SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,pclmul,lzcnt,popcnt")
#endif
/* including simdjson/icelake/bitmanipulation.h: #include "simdjson/icelake/bitmanipulation.h" */
/* begin file simdjson/icelake/bitmanipulation.h */
#ifndef SIMDJSON_ICELAKE_BITMANIPULATION_H
#define SIMDJSON_ICELAKE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BITMANIPULATION_H
/* end file simdjson/icelake/bitmanipulation.h */
/* including simdjson/icelake/bitmask.h: #include "simdjson/icelake/bitmask.h" */
/* begin file simdjson/icelake/bitmask.h */
#ifndef SIMDJSON_ICELAKE_BITMASK_H
#define SIMDJSON_ICELAKE_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processor supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BITMASK_H
/* end file simdjson/icelake/bitmask.h */
/* including simdjson/icelake/simd.h: #include "simdjson/icelake/simd.h" */
/* begin file simdjson/icelake/simd.h */
#ifndef SIMDJSON_ICELAKE_SIMD_H
#define SIMDJSON_ICELAKE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if defined(__GNUC__) && !defined(__clang__)
#if __GNUC__ == 8
#define SIMDJSON_GCC8 1
#endif // __GNUC__ == 8
#endif // defined(__GNUC__) && !defined(__clang__)
#if SIMDJSON_GCC8
/**
* GCC 8 fails to provide _mm512_set_epi8. We roll our own.
*/
inline __m512i _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) {
return _mm512_set_epi64(uint64_t(a7) + (uint64_t(a6) << 8) + (uint64_t(a5) << 16) + (uint64_t(a4) << 24) + (uint64_t(a3) << 32) + (uint64_t(a2) << 40) + (uint64_t(a1) << 48) + (uint64_t(a0) << 56),
uint64_t(a15) + (uint64_t(a14) << 8) + (uint64_t(a13) << 16) + (uint64_t(a12) << 24) + (uint64_t(a11) << 32) + (uint64_t(a10) << 40) + (uint64_t(a9) << 48) + (uint64_t(a8) << 56),
uint64_t(a23) + (uint64_t(a22) << 8) + (uint64_t(a21) << 16) + (uint64_t(a20) << 24) + (uint64_t(a19) << 32) + (uint64_t(a18) << 40) + (uint64_t(a17) << 48) + (uint64_t(a16) << 56),
uint64_t(a31) + (uint64_t(a30) << 8) + (uint64_t(a29) << 16) + (uint64_t(a28) << 24) + (uint64_t(a27) << 32) + (uint64_t(a26) << 40) + (uint64_t(a25) << 48) + (uint64_t(a24) << 56),
uint64_t(a39) + (uint64_t(a38) << 8) + (uint64_t(a37) << 16) + (uint64_t(a36) << 24) + (uint64_t(a35) << 32) + (uint64_t(a34) << 40) + (uint64_t(a33) << 48) + (uint64_t(a32) << 56),
uint64_t(a47) + (uint64_t(a46) << 8) + (uint64_t(a45) << 16) + (uint64_t(a44) << 24) + (uint64_t(a43) << 32) + (uint64_t(a42) << 40) + (uint64_t(a41) << 48) + (uint64_t(a40) << 56),
uint64_t(a55) + (uint64_t(a54) << 8) + (uint64_t(a53) << 16) + (uint64_t(a52) << 24) + (uint64_t(a51) << 32) + (uint64_t(a50) << 40) + (uint64_t(a49) << 48) + (uint64_t(a48) << 56),
uint64_t(a63) + (uint64_t(a62) << 8) + (uint64_t(a61) << 16) + (uint64_t(a60) << 24) + (uint64_t(a59) << 32) + (uint64_t(a58) << 40) + (uint64_t(a57) << 48) + (uint64_t(a56) << 56));
}
#endif // SIMDJSON_GCC8
namespace simdjson {
namespace icelake {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m512i value;
// Zero constructor
simdjson_inline base() : value{__m512i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m512i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m512i&() const { return this->value; }
simdjson_inline operator __m512i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm512_or_si512(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm512_and_si512(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm512_xor_si512(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm512_andnot_si512(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint32_t bitmask_t;
typedef uint64_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m512i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline uint64_t operator==(const simd8<T> lhs, const simd8<T> rhs) {
return _mm512_cmpeq_epi8_mask(lhs, rhs);
}
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
// workaround for compilers unable to figure out that 16 - N is a constant (GCC 8)
constexpr int shift = 16 - N;
return _mm512_alignr_epi8(*this, _mm512_permutex2var_epi64(prev_chunk, _mm512_set_epi64(13, 12, 11, 10, 9, 8, 7, 6), *this), shift);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m512i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm512_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm512_setzero_si512(); }
static simdjson_inline simd8<T> load(const T values[64]) {
return _mm512_loadu_si512(reinterpret_cast<const __m512i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m512i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm512_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm512_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm512_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 64 - count_ones(mask) bytes of the result are significant but 64 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint64_t mask, L * output) const {
// we deliberately avoid _mm512_mask_compressstoreu_epi8 for portability
// (AMD Zen4 has terrible performance with it, it is effectively broken)
// _mm512_mask_compressstoreu_epi8 (output,~mask,*this);
__m512i compressed = _mm512_maskz_compress_epi8(~mask, *this);
_mm512_storeu_si512(output, compressed); // could use a mask
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m512i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[64]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31,
int8_t v32, int8_t v33, int8_t v34, int8_t v35, int8_t v36, int8_t v37, int8_t v38, int8_t v39,
int8_t v40, int8_t v41, int8_t v42, int8_t v43, int8_t v44, int8_t v45, int8_t v46, int8_t v47,
int8_t v48, int8_t v49, int8_t v50, int8_t v51, int8_t v52, int8_t v53, int8_t v54, int8_t v55,
int8_t v56, int8_t v57, int8_t v58, int8_t v59, int8_t v60, int8_t v61, int8_t v62, int8_t v63
) : simd8(_mm512_set_epi8(
v63, v62, v61, v60, v59, v58, v57, v56,
v55, v54, v53, v52, v51, v50, v49, v48,
v47, v46, v45, v44, v43, v42, v41, v40,
v39, v38, v37, v36, v35, v34, v33, v32,
v31, v30, v29, v28, v27, v26, v25, v24,
v23, v22, v21, v20, v19, v18, v17, v16,
v15, v14, v13, v12, v11, v10, v9, v8,
v7, v6, v5, v4, v3, v2, v1, v0
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm512_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm512_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m512i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[64]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31,
uint8_t v32, uint8_t v33, uint8_t v34, uint8_t v35, uint8_t v36, uint8_t v37, uint8_t v38, uint8_t v39,
uint8_t v40, uint8_t v41, uint8_t v42, uint8_t v43, uint8_t v44, uint8_t v45, uint8_t v46, uint8_t v47,
uint8_t v48, uint8_t v49, uint8_t v50, uint8_t v51, uint8_t v52, uint8_t v53, uint8_t v54, uint8_t v55,
uint8_t v56, uint8_t v57, uint8_t v58, uint8_t v59, uint8_t v60, uint8_t v61, uint8_t v62, uint8_t v63
) : simd8(_mm512_set_epi8(
v63, v62, v61, v60, v59, v58, v57, v56,
v55, v54, v53, v52, v51, v50, v49, v48,
v47, v46, v45, v44, v43, v42, v41, v40,
v39, v38, v37, v36, v35, v34, v33, v32,
v31, v30, v29, v28, v27, v26, v25, v24,
v23, v22, v21, v20, v19, v18, v17, v16,
v15, v14, v13, v12, v11, v10, v9, v8,
v7, v6, v5, v4, v3, v2, v1, v0
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm512_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm512_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm512_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm512_min_epu8(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline uint64_t operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline uint64_t operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm512_movepi8_mask(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const {
return !_mm512_test_epi8_mask(*this, *this);
}
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return !_mm512_test_epi8_mask(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm512_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const simd8<T> chunk0) : chunks{chunk0} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(mask, output);
return 64 - count_ones(mask);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0];
}
simdjson_inline simd8x64<T> bit_or(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<T>(
this->chunks[0] | mask
);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return this->chunks[0] == mask;
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return this->chunks[0] == other.chunks[0];
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return this->chunks[0] <= mask;
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_SIMD_H
/* end file simdjson/icelake/simd.h */
/* including simdjson/icelake/stringparsing_defs.h: #include "simdjson/icelake/stringparsing_defs.h" */
/* begin file simdjson/icelake/stringparsing_defs.h */
#ifndef SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
#define SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint64_t bs_bits;
uint64_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 15 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
// store to dest unconditionally - we can overwrite the bits we don't like later
v.store(dst);
return {
static_cast<uint64_t>(v == '\\'), // bs_bits
static_cast<uint64_t>(v == '"'), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
__mmask64 escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
__mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
__mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
__mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
return {
(is_backslash | is_quote | is_control)
};
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
/* end file simdjson/icelake/stringparsing_defs.h */
/* including simdjson/icelake/numberparsing_defs.h: #include "simdjson/icelake/numberparsing_defs.h" */
/* begin file simdjson/icelake/numberparsing_defs.h */
#ifndef SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
#define SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace numberparsing {
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace icelake
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
/* end file simdjson/icelake/numberparsing_defs.h */
/* end file simdjson/icelake/begin.h */
/* including simdjson/generic/amalgamated.h for icelake: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for icelake */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for icelake: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for icelake */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for icelake */
/* including simdjson/generic/jsoncharutils.h for icelake: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for icelake */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for icelake */
/* including simdjson/generic/atomparsing.h for icelake: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for icelake */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace icelake {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for icelake */
/* including simdjson/generic/dom_parser_implementation.h for icelake: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for icelake */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace icelake
} // namespace simdjson
namespace simdjson {
namespace icelake {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for icelake */
/* including simdjson/generic/implementation_simdjson_result_base.h for icelake: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for icelake */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for icelake */
/* including simdjson/generic/numberparsing.h for icelake: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for icelake */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace icelake {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for icelake */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for icelake: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for icelake */
/* end file simdjson/generic/amalgamated.h for icelake */
/* including simdjson/icelake/end.h: #include "simdjson/icelake/end.h" */
/* begin file simdjson/icelake/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_ICELAKE
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "icelake" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/icelake/end.h */
#endif // SIMDJSON_ICELAKE_H
/* end file simdjson/icelake.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64)
/* including simdjson/ppc64.h: #include "simdjson/ppc64.h" */
/* begin file simdjson/ppc64.h */
#ifndef SIMDJSON_PPC64_H
#define SIMDJSON_PPC64_H
/* including simdjson/ppc64/begin.h: #include "simdjson/ppc64/begin.h" */
/* begin file simdjson/ppc64/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "ppc64" */
#define SIMDJSON_IMPLEMENTATION ppc64
/* including simdjson/ppc64/base.h: #include "simdjson/ppc64/base.h" */
/* begin file simdjson/ppc64/base.h */
#ifndef SIMDJSON_PPC64_BASE_H
#define SIMDJSON_PPC64_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for ALTIVEC (PPC64).
*/
namespace ppc64 {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_BASE_H
/* end file simdjson/ppc64/base.h */
/* including simdjson/ppc64/intrinsics.h: #include "simdjson/ppc64/intrinsics.h" */
/* begin file simdjson/ppc64/intrinsics.h */
#ifndef SIMDJSON_PPC64_INTRINSICS_H
#define SIMDJSON_PPC64_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// This should be the correct header whether
// you use visual studio or other compilers.
#include <altivec.h>
// These are defined by altivec.h in GCC toolchain, it is safe to undef them.
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "insufficient padding for ppc64");
#endif // SIMDJSON_PPC64_INTRINSICS_H
/* end file simdjson/ppc64/intrinsics.h */
/* including simdjson/ppc64/bitmanipulation.h: #include "simdjson/ppc64/bitmanipulation.h" */
/* begin file simdjson/ppc64/bitmanipulation.h */
#ifndef SIMDJSON_PPC64_BITMANIPULATION_H
#define SIMDJSON_PPC64_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num - 1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline int count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num); // Visual Studio wants two underscores
}
#else
simdjson_inline int count_ones(uint64_t input_num) {
return __builtin_popcountll(input_num);
}
#endif
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_BITMANIPULATION_H
/* end file simdjson/ppc64/bitmanipulation.h */
/* including simdjson/ppc64/bitmask.h: #include "simdjson/ppc64/bitmask.h" */
/* begin file simdjson/ppc64/bitmask.h */
#ifndef SIMDJSON_PPC64_BITMASK_H
#define SIMDJSON_PPC64_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is
// encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
// You can use the version below, however gcc sometimes miscompiles
// vec_pmsum_be, it happens somewhere around between 8 and 9th version.
// The performance boost was not noticeable, falling back to a usual
// implementation.
// __vector unsigned long long all_ones = {~0ull, ~0ull};
// __vector unsigned long long mask = {bitmask, 0};
// // Clang and GCC return different values for pmsum for ull so cast it to one.
// // Generally it is not specified by ALTIVEC ISA what is returned by
// // vec_pmsum_be.
// #if defined(__LITTLE_ENDIAN__)
// return (uint64_t)(((__vector unsigned long long)vec_pmsum_be(all_ones, mask))[0]);
// #else
// return (uint64_t)(((__vector unsigned long long)vec_pmsum_be(all_ones, mask))[1]);
// #endif
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif
/* end file simdjson/ppc64/bitmask.h */
/* including simdjson/ppc64/numberparsing_defs.h: #include "simdjson/ppc64/numberparsing_defs.h" */
/* begin file simdjson/ppc64/numberparsing_defs.h */
#ifndef SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
#define SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#if defined(__linux__)
#include <byteswap.h>
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#endif
namespace simdjson {
namespace ppc64 {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
#ifdef __BIG_ENDIAN__
#if defined(__linux__)
val = bswap_64(val);
#elif defined(__FreeBSD__)
val = bswap64(val);
#endif
#endif
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace ppc64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
/* end file simdjson/ppc64/numberparsing_defs.h */
/* including simdjson/ppc64/simd.h: #include "simdjson/ppc64/simd.h" */
/* begin file simdjson/ppc64/simd.h */
#ifndef SIMDJSON_PPC64_SIMD_H
#define SIMDJSON_PPC64_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace ppc64 {
namespace {
namespace simd {
using __m128i = __vector unsigned char;
template <typename Child> struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i &() const {
return this->value;
}
simdjson_inline operator __m128i &() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const {
return vec_or(this->value, (__m128i)other);
}
simdjson_inline Child operator&(const Child other) const {
return vec_and(this->value, (__m128i)other);
}
simdjson_inline Child operator^(const Child other) const {
return vec_xor(this->value, (__m128i)other);
}
simdjson_inline Child bit_andnot(const Child other) const {
return vec_andc(this->value, (__m128i)other);
}
simdjson_inline Child &operator|=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast | other;
return *this_cast;
}
simdjson_inline Child &operator&=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast & other;
return *this_cast;
}
simdjson_inline Child &operator^=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast ^ other;
return *this_cast;
}
};
template <typename T, typename Mask = simd8<bool>>
struct base8 : base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) {
return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs);
}
static const int SIZE = sizeof(base<simd8<T>>::value);
template <int N = 1>
simdjson_inline simd8<T> prev(simd8<T> prev_chunk) const {
__m128i chunk = this->value;
#ifdef __LITTLE_ENDIAN__
chunk = (__m128i)vec_reve(this->value);
prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk);
#endif
chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N);
#ifdef __LITTLE_ENDIAN__
chunk = (__m128i)vec_reve((__m128i)chunk);
#endif
return chunk;
}
};
// SIMD byte mask type (returned by things like eq and gt)
template <> struct simd8<bool> : base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) {
return (__m128i)vec_splats((unsigned char)(-(!!_value)));
}
simdjson_inline simd8() : base8<bool>() {}
simdjson_inline simd8(const __m128i _value)
: base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value)
: base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const {
__vector unsigned long long result;
const __m128i perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40,
0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x00};
result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value,
(__m128i)perm_mask));
#ifdef __LITTLE_ENDIAN__
return static_cast<int>(result[1]);
#else
return static_cast<int>(result[0]);
#endif
}
simdjson_inline bool any() const {
return !vec_all_eq(this->value, (__m128i)vec_splats(0));
}
simdjson_inline simd8<bool> operator~() const {
return this->value ^ (__m128i)splat(true);
}
};
template <typename T> struct base8_numeric : base8<T> {
static simdjson_inline simd8<T> splat(T value) {
(void)value;
return (__m128i)vec_splats(value);
}
static simdjson_inline simd8<T> zero() { return splat(0); }
static simdjson_inline simd8<T> load(const T values[16]) {
return (__m128i)(vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(values)));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(T v0, T v1, T v2, T v3, T v4,
T v5, T v6, T v7, T v8, T v9,
T v10, T v11, T v12, T v13,
T v14, T v15) {
return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
v14, v15);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value)
: base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const {
vec_vsx_st(this->value, 0, reinterpret_cast<__m128i *>(dst));
}
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const {
return (__m128i)((__m128i)this->value + (__m128i)other);
}
simdjson_inline simd8<T> operator-(const simd8<T> other) const {
return (__m128i)((__m128i)this->value - (__m128i)other);
}
simdjson_inline simd8<T> &operator+=(const simd8<T> other) {
*this = *this + other;
return *static_cast<simd8<T> *>(this);
}
simdjson_inline simd8<T> &operator-=(const simd8<T> other) {
*this = *this - other;
return *static_cast<simd8<T> *>(this);
}
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior
// for out of range values)
template <typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted
// as a bitset). Passing a 0 value for mask would be equivalent to writing out
// every byte to output. Only the first 16 - count_ones(mask) bytes of the
// result are significant but 16 bytes get written. Design consideration: it
// seems like a function with the signature simd8<L> compress(uint32_t mask)
// would be sensible, but the AVX ISA makes this kind of approach difficult.
template <typename L>
simdjson_inline void compress(uint16_t mask, L *output) const {
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
using internal::thintable_epi8;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
#ifdef __LITTLE_ENDIAN__
__m128i shufmask = (__m128i)(__vector unsigned long long){
thintable_epi8[mask1], thintable_epi8[mask2]};
#else
__m128i shufmask = (__m128i)(__vector unsigned long long){
thintable_epi8[mask2], thintable_epi8[mask1]};
shufmask = (__m128i)vec_reve((__m128i)shufmask);
#endif
// we increment by 0x08 the second half of the mask
shufmask = ((__m128i)shufmask) +
((__m128i)(__vector int){0, 0, 0x08080808, 0x08080808});
// this is the version "nearly pruned"
__m128i pruned = vec_perm(this->value, this->value, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
__m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask);
vec_vsx_st(answer, 0, reinterpret_cast<__m128i *>(output));
}
template <typename L>
simdjson_inline simd8<L>
lookup_16(L replace0, L replace1, L replace2, L replace3, L replace4,
L replace5, L replace6, L replace7, L replace8, L replace9,
L replace10, L replace11, L replace12, L replace13, L replace14,
L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3, replace4, replace5, replace6,
replace7, replace8, replace9, replace10, replace11, replace12,
replace13, replace14, replace15));
}
};
// Signed bytes
template <> struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value)
: base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t *values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(int8_t v0, int8_t v1, int8_t v2, int8_t v3,
int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11,
int8_t v12, int8_t v13, int8_t v14, int8_t v15)
: simd8((__m128i)(__vector signed char){v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10, v11, v12, v13, v14,
v15}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t>
repeat_16(int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5,
int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11,
int8_t v12, int8_t v13, int8_t v14, int8_t v15) {
return simd8<int8_t>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t>
max_val(const simd8<int8_t> other) const {
return (__m128i)vec_max((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<int8_t>
min_val(const simd8<int8_t> other) const {
return (__m128i)vec_min((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<bool>
operator>(const simd8<int8_t> other) const {
return (__m128i)vec_cmpgt((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<bool>
operator<(const simd8<int8_t> other) const {
return (__m128i)vec_cmplt((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
};
// Unsigned bytes
template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value)
: base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t *values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline
simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5,
uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10,
uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15)
: simd8((__m128i){v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t>
repeat_16(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4,
uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9,
uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14,
uint8_t v15) {
return simd8<uint8_t>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15);
}
// Saturated math
simdjson_inline simd8<uint8_t>
saturating_add(const simd8<uint8_t> other) const {
return (__m128i)vec_adds(this->value, (__m128i)other);
}
simdjson_inline simd8<uint8_t>
saturating_sub(const simd8<uint8_t> other) const {
return (__m128i)vec_subs(this->value, (__m128i)other);
}
// Order-specific operations
simdjson_inline simd8<uint8_t>
max_val(const simd8<uint8_t> other) const {
return (__m128i)vec_max(this->value, (__m128i)other);
}
simdjson_inline simd8<uint8_t>
min_val(const simd8<uint8_t> other) const {
return (__m128i)vec_min(this->value, (__m128i)other);
}
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t>
gt_bits(const simd8<uint8_t> other) const {
return this->saturating_sub(other);
}
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t>
lt_bits(const simd8<uint8_t> other) const {
return other.saturating_sub(*this);
}
simdjson_inline simd8<bool>
operator<=(const simd8<uint8_t> other) const {
return other.max_val(*this) == other;
}
simdjson_inline simd8<bool>
operator>=(const simd8<uint8_t> other) const {
return other.min_val(*this) == other;
}
simdjson_inline simd8<bool>
operator>(const simd8<uint8_t> other) const {
return this->gt_bits(other).any_bits_set();
}
simdjson_inline simd8<bool>
operator<(const simd8<uint8_t> other) const {
return this->gt_bits(other).any_bits_set();
}
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const {
return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0)));
}
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const {
return (*this & bits).bits_not_set();
}
simdjson_inline simd8<bool> any_bits_set() const {
return ~this->bits_not_set();
}
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const {
return ~this->bits_not_set(bits);
}
simdjson_inline bool bits_not_set_anywhere() const {
return vec_all_eq(this->value, (__m128i)vec_splats(0));
}
simdjson_inline bool any_bits_set_anywhere() const {
return !bits_not_set_anywhere();
}
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
return vec_all_eq(vec_and(this->value, (__m128i)bits),
(__m128i)vec_splats(0));
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const {
return !bits_not_set_anywhere(bits);
}
template <int N> simdjson_inline simd8<uint8_t> shr() const {
return simd8<uint8_t>(
(__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N)));
}
template <int N> simdjson_inline simd8<uint8_t> shl() const {
return simd8<uint8_t>(
(__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N)));
}
};
template <typename T> struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4,
"PPC64 kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T> &o) = delete; // no copy allowed
simd8x64<T> &
operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1,
const simd8<T> chunk2, const simd8<T> chunk3)
: chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64])
: chunks{simd8<T>::load(ptr), simd8<T>::load(ptr + 16),
simd8<T>::load(ptr + 32), simd8<T>::load(ptr + 48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr + sizeof(simd8<T>) * 0);
this->chunks[1].store(ptr + sizeof(simd8<T>) * 1);
this->chunks[2].store(ptr + sizeof(simd8<T>) * 2);
this->chunks[3].store(ptr + sizeof(simd8<T>) * 3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) |
(this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T *output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16),
output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32),
output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48),
output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
uint64_t r1 = this->chunks[1].to_bitmask();
uint64_t r2 = this->chunks[2].to_bitmask();
uint64_t r3 = this->chunks[3].to_bitmask();
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(this->chunks[0] == mask, this->chunks[1] == mask,
this->chunks[2] == mask, this->chunks[3] == mask)
.to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3])
.to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(this->chunks[0] <= mask, this->chunks[1] <= mask,
this->chunks[2] <= mask, this->chunks[3] <= mask)
.to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_SIMD_INPUT_H
/* end file simdjson/ppc64/simd.h */
/* including simdjson/ppc64/stringparsing_defs.h: #include "simdjson/ppc64/stringparsing_defs.h" */
/* begin file simdjson/ppc64/stringparsing_defs.h */
#ifndef SIMDJSON_PPC64_STRINGPARSING_DEFS_H
#define SIMDJSON_PPC64_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/simd.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote
copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() {
return ((bs_bits - 1) & quote_bits) != 0;
}
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() {
return trailing_zeroes(quote_bits);
}
simdjson_inline int backslash_index() {
return trailing_zeroes(bs_bits);
}
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote
backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1),
"backslash and quote finder must process fewer than "
"SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on
// PPC; therefore, we smash them together into a 64-byte mask and get the
// bitmask from there.
uint64_t bs_and_quote =
simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
// We store it as a 64-bit bitmask even though we only need 16 bits.
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_STRINGPARSING_DEFS_H
/* end file simdjson/ppc64/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/ppc64/begin.h */
/* including simdjson/generic/amalgamated.h for ppc64: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for ppc64 */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for ppc64: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for ppc64 */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for ppc64 */
/* including simdjson/generic/jsoncharutils.h for ppc64: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for ppc64 */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for ppc64 */
/* including simdjson/generic/atomparsing.h for ppc64: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace ppc64 {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for ppc64 */
/* including simdjson/generic/dom_parser_implementation.h for ppc64: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for ppc64 */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
namespace ppc64 {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for ppc64 */
/* including simdjson/generic/implementation_simdjson_result_base.h for ppc64: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for ppc64 */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for ppc64 */
/* including simdjson/generic/numberparsing.h for ppc64: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for ppc64 */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace ppc64 {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for ppc64 */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for ppc64 */
/* end file simdjson/generic/amalgamated.h for ppc64 */
/* including simdjson/ppc64/end.h: #include "simdjson/ppc64/end.h" */
/* begin file simdjson/ppc64/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "ppc64" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/ppc64/end.h */
#endif // SIMDJSON_PPC64_H
/* end file simdjson/ppc64.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
/* including simdjson/westmere.h: #include "simdjson/westmere.h" */
/* begin file simdjson/westmere.h */
#ifndef SIMDJSON_WESTMERE_H
#define SIMDJSON_WESTMERE_H
/* including simdjson/westmere/begin.h: #include "simdjson/westmere/begin.h" */
/* begin file simdjson/westmere/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "westmere" */
#define SIMDJSON_IMPLEMENTATION westmere
/* including simdjson/westmere/base.h: #include "simdjson/westmere/base.h" */
/* begin file simdjson/westmere/base.h */
#ifndef SIMDJSON_WESTMERE_BASE_H
#define SIMDJSON_WESTMERE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE
namespace simdjson {
/**
* Implementation for Westmere (Intel SSE4.2).
*/
namespace westmere {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BASE_H
/* end file simdjson/westmere/base.h */
/* including simdjson/westmere/intrinsics.h: #include "simdjson/westmere/intrinsics.h" */
/* begin file simdjson/westmere/intrinsics.h */
#ifndef SIMDJSON_WESTMERE_INTRINSICS_H
#define SIMDJSON_WESTMERE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
*/
#include <smmintrin.h> // for _mm_alignr_epi8
#include <wmmintrin.h> // for _mm_clmulepi64_si128
#endif
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere");
#endif // SIMDJSON_WESTMERE_INTRINSICS_H
/* end file simdjson/westmere/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE
SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt")
#endif
/* including simdjson/westmere/bitmanipulation.h: #include "simdjson/westmere/bitmanipulation.h" */
/* begin file simdjson/westmere/bitmanipulation.h */
#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H
#define SIMDJSON_WESTMERE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H
/* end file simdjson/westmere/bitmanipulation.h */
/* including simdjson/westmere/bitmask.h: #include "simdjson/westmere/bitmask.h" */
/* begin file simdjson/westmere/bitmask.h */
#ifndef SIMDJSON_WESTMERE_BITMASK_H
#define SIMDJSON_WESTMERE_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processing supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMASK_H
/* end file simdjson/westmere/bitmask.h */
/* including simdjson/westmere/numberparsing_defs.h: #include "simdjson/westmere/numberparsing_defs.h" */
/* begin file simdjson/westmere/numberparsing_defs.h */
#ifndef SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
#define SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
/* including simdjson/westmere/base.h: #include "simdjson/westmere/base.h" */
/* begin file simdjson/westmere/base.h */
#ifndef SIMDJSON_WESTMERE_BASE_H
#define SIMDJSON_WESTMERE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE
namespace simdjson {
/**
* Implementation for Westmere (Intel SSE4.2).
*/
namespace westmere {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BASE_H
/* end file simdjson/westmere/base.h */
/* including simdjson/westmere/intrinsics.h: #include "simdjson/westmere/intrinsics.h" */
/* begin file simdjson/westmere/intrinsics.h */
#ifndef SIMDJSON_WESTMERE_INTRINSICS_H
#define SIMDJSON_WESTMERE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
*/
#include <smmintrin.h> // for _mm_alignr_epi8
#include <wmmintrin.h> // for _mm_clmulepi64_si128
#endif
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere");
#endif // SIMDJSON_WESTMERE_INTRINSICS_H
/* end file simdjson/westmere/intrinsics.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace numberparsing {
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace westmere
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
/* end file simdjson/westmere/numberparsing_defs.h */
/* including simdjson/westmere/simd.h: #include "simdjson/westmere/simd.h" */
/* begin file simdjson/westmere/simd.h */
#ifndef SIMDJSON_WESTMERE_SIMD_H
#define SIMDJSON_WESTMERE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
namespace simd {
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm_setzero_si128(); }
static simdjson_inline simd8<T> load(const T values[16]) {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m128i shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask
shufmask =
_mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m128i pruned = _mm_shuffle_epi8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8));
__m128i answer = _mm_shuffle_epi8(pruned, compactmask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm_min_epu8(*this, other); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm_testz_si128(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask() );
uint64_t r1 = this->chunks[1].to_bitmask() ;
uint64_t r2 = this->chunks[2].to_bitmask() ;
uint64_t r3 = this->chunks[3].to_bitmask() ;
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H
/* end file simdjson/westmere/simd.h */
/* including simdjson/westmere/stringparsing_defs.h: #include "simdjson/westmere/stringparsing_defs.h" */
/* begin file simdjson/westmere/stringparsing_defs.h */
#ifndef SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
#define SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
/* including simdjson/westmere/bitmanipulation.h: #include "simdjson/westmere/bitmanipulation.h" */
/* begin file simdjson/westmere/bitmanipulation.h */
#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H
#define SIMDJSON_WESTMERE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H
/* end file simdjson/westmere/bitmanipulation.h */
/* including simdjson/westmere/simd.h: #include "simdjson/westmere/simd.h" */
/* begin file simdjson/westmere/simd.h */
#ifndef SIMDJSON_WESTMERE_SIMD_H
#define SIMDJSON_WESTMERE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
namespace simd {
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm_setzero_si128(); }
static simdjson_inline simd8<T> load(const T values[16]) {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m128i shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask
shufmask =
_mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m128i pruned = _mm_shuffle_epi8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8));
__m128i answer = _mm_shuffle_epi8(pruned, compactmask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm_min_epu8(*this, other); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm_testz_si128(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask() );
uint64_t r1 = this->chunks[1].to_bitmask() ;
uint64_t r2 = this->chunks[2].to_bitmask() ;
uint64_t r3 = this->chunks[3].to_bitmask() ;
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H
/* end file simdjson/westmere/simd.h */
namespace simdjson {
namespace westmere {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + 16);
v0.store(dst);
v1.store(dst + 16);
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
/* end file simdjson/westmere/stringparsing_defs.h */
/* end file simdjson/westmere/begin.h */
/* including simdjson/generic/amalgamated.h for westmere: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for westmere */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for westmere: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for westmere */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for westmere */
/* including simdjson/generic/jsoncharutils.h for westmere: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for westmere */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for westmere */
/* including simdjson/generic/atomparsing.h for westmere: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for westmere */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace westmere {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for westmere */
/* including simdjson/generic/dom_parser_implementation.h for westmere: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for westmere */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace westmere
} // namespace simdjson
namespace simdjson {
namespace westmere {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for westmere */
/* including simdjson/generic/implementation_simdjson_result_base.h for westmere: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for westmere */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for westmere */
/* including simdjson/generic/numberparsing.h for westmere: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for westmere */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace westmere {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for westmere */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for westmere: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for westmere */
/* end file simdjson/generic/amalgamated.h for westmere */
/* including simdjson/westmere/end.h: #include "simdjson/westmere/end.h" */
/* begin file simdjson/westmere/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "westmere" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/westmere/end.h */
#endif // SIMDJSON_WESTMERE_H
/* end file simdjson/westmere.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
/* including simdjson/lasx.h: #include "simdjson/lasx.h" */
/* begin file simdjson/lasx.h */
#ifndef SIMDJSON_LASX_H
#define SIMDJSON_LASX_H
/* including simdjson/lasx/begin.h: #include "simdjson/lasx/begin.h" */
/* begin file simdjson/lasx/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "lasx" */
#define SIMDJSON_IMPLEMENTATION lasx
#include <lsxintrin.h> // This is a hack. We should not need to put this include here.
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
// nothing needed.
#else
SIMDJSON_TARGET_REGION("lasx,lsx")
#endif
/* including simdjson/lasx/base.h: #include "simdjson/lasx/base.h" */
/* begin file simdjson/lasx/base.h */
#ifndef SIMDJSON_LASX_BASE_H
#define SIMDJSON_LASX_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for LASX.
*/
namespace lasx {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_BASE_H
/* end file simdjson/lasx/base.h */
/* including simdjson/lasx/intrinsics.h: #include "simdjson/lasx/intrinsics.h" */
/* begin file simdjson/lasx/intrinsics.h */
#ifndef SIMDJSON_LASX_INTRINSICS_H
#define SIMDJSON_LASX_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <lsxintrin.h>
#include <lasxintrin.h>
static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch ASX");
#endif // SIMDJSON_LASX_INTRINSICS_H
/* end file simdjson/lasx/intrinsics.h */
/* including simdjson/lasx/bitmanipulation.h: #include "simdjson/lasx/bitmanipulation.h" */
/* begin file simdjson/lasx/bitmanipulation.h */
#ifndef SIMDJSON_LASX_BITMANIPULATION_H
#define SIMDJSON_LASX_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
return __lasx_xvpickve2gr_w(__lasx_xvpcnt_d(__m256i(v4u64{input_num, 0, 0, 0})), 0);
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_BITMANIPULATION_H
/* end file simdjson/lasx/bitmanipulation.h */
/* including simdjson/lasx/bitmask.h: #include "simdjson/lasx/bitmask.h" */
/* begin file simdjson/lasx/bitmask.h */
#ifndef SIMDJSON_LASX_BITMASK_H
#define SIMDJSON_LASX_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif
/* end file simdjson/lasx/bitmask.h */
/* including simdjson/lasx/numberparsing_defs.h: #include "simdjson/lasx/numberparsing_defs.h" */
/* begin file simdjson/lasx/numberparsing_defs.h */
#ifndef SIMDJSON_LASX_NUMBERPARSING_DEFS_H
#define SIMDJSON_LASX_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace lasx {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace lasx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H
/* end file simdjson/lasx/numberparsing_defs.h */
/* including simdjson/lasx/simd.h: #include "simdjson/lasx/simd.h" */
/* begin file simdjson/lasx/simd.h */
#ifndef SIMDJSON_LASX_SIMD_H
#define SIMDJSON_LASX_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m256i value;
// Zero constructor
simdjson_inline base() : value{__m256i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m256i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m256i&() const { return this->value; }
simdjson_inline operator __m256i&() { return this->value; }
simdjson_inline operator const v32i8&() const { return (v32i8&)this->value; }
simdjson_inline operator v32i8&() { return (v32i8&)this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return __lasx_xvor_v(*this, other); }
simdjson_inline Child operator&(const Child other) const { return __lasx_xvand_v(*this, other); }
simdjson_inline Child operator^(const Child other) const { return __lasx_xvxor_v(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return __lasx_xvandn_v(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return __lasx_xvseq_b(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
__m256i hi = __lasx_xvbsll_v(*this, N);
__m256i lo = __lasx_xvbsrl_v(*this, 16 - N);
__m256i tmp = __lasx_xvbsrl_v(prev_chunk, 16 - N);
lo = __lasx_xvpermi_q(lo, tmp, 0x21);
return __lasx_xvor_v(hi, lo);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const {
__m256i mask = __lasx_xvmskltz_b(*this);
return (__lasx_xvpickve2gr_w(mask, 4) << 16) | (__lasx_xvpickve2gr_w(mask, 0));
}
simdjson_inline bool any() const {
__m256i v = __lasx_xvmsknz_b(*this);
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) {
return __lasx_xvreplgr2vr_b(_value);
}
static simdjson_inline simd8<T> zero() { return __lasx_xvldi(0); }
static simdjson_inline simd8<T> load(const T values[32]) {
return __lasx_xvld(reinterpret_cast<const __m256i *>(values), 0);
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[32]) const {
return __lasx_xvst(*this, reinterpret_cast<__m256i *>(dst), 0);
}
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return __lasx_xvadd_b(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return __lasx_xvsub_b(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __lasx_xvshuf_b(lookup_table, lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
template<typename L>
simdjson_inline void compress(uint32_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by haswell
// lasx do it in 4 steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second significant 8 bits
uint8_t mask3 = uint8_t(mask >> 16); // ...
uint8_t mask4 = uint8_t(mask >> 24); // ...
// next line just loads the 64-bit values thintable_epi8[mask{1,2,3,4}]
// into a 256-bit register.
__m256i shufmask = {int64_t(thintable_epi8[mask1]), int64_t(thintable_epi8[mask2]) + 0x0808080808080808, int64_t(thintable_epi8[mask3]), int64_t(thintable_epi8[mask4]) + 0x0808080808080808};
// this is the version "nearly pruned"
__m256i pruned = __lasx_xvshuf_b(*this, *this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
int pop2 = BitsSetTable256mul2[mask2];
int pop3 = BitsSetTable256mul2[mask3];
// then load the corresponding mask
__m256i masklo = __lasx_xvldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop1 * 8);
__m256i maskhi = __lasx_xvldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop3 * 8);
__m256i compactmask = __lasx_xvpermi_q(maskhi, masklo, 0x20);
__m256i answer = __lasx_xvshuf_b(pruned, pruned, compactmask);
__lasx_xvst(answer, reinterpret_cast<uint8_t*>(output), 0);
uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2);
uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3);
uint64_t *pos = reinterpret_cast<uint64_t*>(reinterpret_cast<uint8_t*>(output) + 16 - (pop1 + pop2) / 2);
pos[0] = value3;
pos[1] = value4;
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31
) : simd8({
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return __lasx_xvmax_b(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return __lasx_xvmin_b(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return __lasx_xvslt_b(other, *this); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return __lasx_xvslt_b(*this, other); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31
) : simd8(__m256i(v32u8{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
})) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __lasx_xvsadd_bu(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __lasx_xvssub_bu(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return __lasx_xvmax_bu(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return __lasx_xvmin_bu(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const {
__m256i mask = __lasx_xvmskltz_b(*this);
return (0 == __lasx_xvpickve2gr_w(mask, 0)) && (0 == __lasx_xvpickve2gr_w(mask, 4));
}
simdjson_inline bool bits_not_set_anywhere() const {
__m256i v = __lasx_xvmsknz_b(*this);
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
__m256i v = __lasx_xvmsknz_b(__lasx_xvand_v(*this, bits));
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(__lasx_xvsrli_b(*this, N)); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(__lasx_xvslli_b(*this, N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+32)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint32_t mask1 = uint32_t(mask);
uint32_t mask2 = uint32_t(mask >> 32);
__m256i zcnt = __lasx_xvpcnt_w(__m256i(v4u64{~mask, 0, 0, 0}));
uint64_t zcnt1 = __lasx_xvpickve2gr_wu(zcnt, 0);
uint64_t zcnt2 = __lasx_xvpickve2gr_wu(zcnt, 1);
// There should be a critical value which processes in scaler is faster.
if (zcnt1)
this->chunks[0].compress(mask1, output);
if (zcnt2)
this->chunks[1].compress(mask2, output + zcnt1);
return zcnt1 + zcnt2;
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
}
simdjson_inline uint64_t to_bitmask() const {
__m256i mask0 = __lasx_xvmskltz_b(this->chunks[0]);
__m256i mask1 = __lasx_xvmskltz_b(this->chunks[1]);
__m256i mask_tmp = __lasx_xvpickve_w(mask0, 4);
__m256i tmp = __lasx_xvpickve_w(mask1, 4);
mask0 = __lasx_xvinsve0_w(mask0, mask1, 1);
mask_tmp = __lasx_xvinsve0_w(mask_tmp, tmp, 1);
return __lasx_xvpickve2gr_du(__lasx_xvpackev_h(mask_tmp, mask0), 0);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0] | this->chunks[1];
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_SIMD_H
/* end file simdjson/lasx/simd.h */
/* including simdjson/lasx/stringparsing_defs.h: #include "simdjson/lasx/stringparsing_defs.h" */
/* begin file simdjson/lasx/stringparsing_defs.h */
#ifndef SIMDJSON_LASX_STRINGPARSING_DEFS_H
#define SIMDJSON_LASX_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return {
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_STRINGPARSING_DEFS_H
/* end file simdjson/lasx/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/lasx/begin.h */
/* including simdjson/generic/amalgamated.h for lasx: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for lasx */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for lasx: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for lasx */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for lasx */
/* including simdjson/generic/jsoncharutils.h for lasx: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for lasx */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for lasx */
/* including simdjson/generic/atomparsing.h for lasx: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for lasx */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace lasx {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for lasx */
/* including simdjson/generic/dom_parser_implementation.h for lasx: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for lasx */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace lasx
} // namespace simdjson
namespace simdjson {
namespace lasx {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for lasx */
/* including simdjson/generic/implementation_simdjson_result_base.h for lasx: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for lasx */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for lasx */
/* including simdjson/generic/numberparsing.h for lasx: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for lasx */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace lasx {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for lasx */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for lasx: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for lasx */
/* end file simdjson/generic/amalgamated.h for lasx */
/* including simdjson/lasx/end.h: #include "simdjson/lasx/end.h" */
/* begin file simdjson/lasx/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "lasx" */
#undef SIMDJSON_IMPLEMENTATION
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
// nothing needed.
#else
SIMDJSON_UNTARGET_REGION
#endif
/* end file simdjson/lasx/end.h */
#endif // SIMDJSON_LASX_H
/* end file simdjson/lasx.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
/* including simdjson/lsx.h: #include "simdjson/lsx.h" */
/* begin file simdjson/lsx.h */
#ifndef SIMDJSON_LSX_H
#define SIMDJSON_LSX_H
/* including simdjson/lsx/begin.h: #include "simdjson/lsx/begin.h" */
/* begin file simdjson/lsx/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "lsx" */
#define SIMDJSON_IMPLEMENTATION lsx
/* including simdjson/lsx/base.h: #include "simdjson/lsx/base.h" */
/* begin file simdjson/lsx/base.h */
#ifndef SIMDJSON_LSX_BASE_H
#define SIMDJSON_LSX_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for LSX.
*/
namespace lsx {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_BASE_H
/* end file simdjson/lsx/base.h */
/* including simdjson/lsx/intrinsics.h: #include "simdjson/lsx/intrinsics.h" */
/* begin file simdjson/lsx/intrinsics.h */
#ifndef SIMDJSON_LSX_INTRINSICS_H
#define SIMDJSON_LSX_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <lsxintrin.h>
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch SX");
#endif // SIMDJSON_LSX_INTRINSICS_H
/* end file simdjson/lsx/intrinsics.h */
/* including simdjson/lsx/bitmanipulation.h: #include "simdjson/lsx/bitmanipulation.h" */
/* begin file simdjson/lsx/bitmanipulation.h */
#ifndef SIMDJSON_LSX_BITMANIPULATION_H
#define SIMDJSON_LSX_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
return __lsx_vpickve2gr_w(__lsx_vpcnt_d(__m128i(v2u64{input_num, 0})), 0);
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_BITMANIPULATION_H
/* end file simdjson/lsx/bitmanipulation.h */
/* including simdjson/lsx/bitmask.h: #include "simdjson/lsx/bitmask.h" */
/* begin file simdjson/lsx/bitmask.h */
#ifndef SIMDJSON_LSX_BITMASK_H
#define SIMDJSON_LSX_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif
/* end file simdjson/lsx/bitmask.h */
/* including simdjson/lsx/numberparsing_defs.h: #include "simdjson/lsx/numberparsing_defs.h" */
/* begin file simdjson/lsx/numberparsing_defs.h */
#ifndef SIMDJSON_LSX_NUMBERPARSING_DEFS_H
#define SIMDJSON_LSX_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace lsx {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace lsx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H
/* end file simdjson/lsx/numberparsing_defs.h */
/* including simdjson/lsx/simd.h: #include "simdjson/lsx/simd.h" */
/* begin file simdjson/lsx/simd.h */
#ifndef SIMDJSON_LSX_SIMD_H
#define SIMDJSON_LSX_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
simdjson_inline operator const v16i8&() const { return (v16i8&)this->value; }
simdjson_inline operator v16i8&() { return (v16i8&)this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return __lsx_vor_v(*this, other); }
simdjson_inline Child operator&(const Child other) const { return __lsx_vand_v(*this, other); }
simdjson_inline Child operator^(const Child other) const { return __lsx_vxor_v(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return __lsx_vandn_v(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return __lsx_vseq_b(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return __lsx_vor_v(__lsx_vbsll_v(*this, N), __lsx_vbsrl_v(prev_chunk, 16 - N));
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) {
return __lsx_vreplgr2vr_b(uint8_t(-(!!_value)));
}
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return __lsx_vpickve2gr_w(__lsx_vmskltz_b(*this), 0); }
simdjson_inline bool any() const { return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(*this), 0); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return __lsx_vreplgr2vr_b(_value); }
static simdjson_inline simd8<T> zero() { return __lsx_vldi(0); }
static simdjson_inline simd8<T> load(const T values[16]) {
return __lsx_vld(reinterpret_cast<const __m128i *>(values), 0);
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const {
return __lsx_vst(*this, reinterpret_cast<__m128i *>(dst), 0);
}
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return __lsx_vadd_b(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return __lsx_vsub_b(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __lsx_vshuf_b(lookup_table, lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by haswell
// lsx do it in 2 steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second least significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register.
__m128i shufmask = {int64_t(thintable_epi8[mask1]), int64_t(thintable_epi8[mask2]) + 0x0808080808080808};
// this is the version "nearly pruned"
__m128i pruned = __lsx_vshuf_b(*this, *this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask
__m128i compactmask = __lsx_vldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop1 * 8);
__m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask);
__lsx_vst(answer, reinterpret_cast<uint8_t*>(output), 0);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[16]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8({
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return __lsx_vmax_b(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return __lsx_vmin_b(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return __lsx_vslt_b(other, *this); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return __lsx_vslt_b(*this, other); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[16]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(__m128i(v16u8{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
})) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __lsx_vsadd_bu(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __lsx_vssub_bu(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return __lsx_vmax_bu(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return __lsx_vmin_bu(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return 0 == __lsx_vpickve2gr_w(__lsx_vmskltz_b(*this), 0); }
simdjson_inline bool bits_not_set_anywhere() const { return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(*this), 0); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0);
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(__lsx_vsrli_b(*this, N)); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(__lsx_vslli_b(*this, N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint16_t mask1 = uint16_t(mask);
uint16_t mask2 = uint16_t(mask >> 16);
uint16_t mask3 = uint16_t(mask >> 32);
uint16_t mask4 = uint16_t(mask >> 48);
__m128i zcnt = __lsx_vpcnt_h(__m128i(v2u64{~mask, 0}));
uint64_t zcnt1 = __lsx_vpickve2gr_hu(zcnt, 0);
uint64_t zcnt2 = __lsx_vpickve2gr_hu(zcnt, 1);
uint64_t zcnt3 = __lsx_vpickve2gr_hu(zcnt, 2);
uint64_t zcnt4 = __lsx_vpickve2gr_hu(zcnt, 3);
uint8_t *voutput = reinterpret_cast<uint8_t*>(output);
// There should be a critical value which processes in scaler is faster.
if (zcnt1)
this->chunks[0].compress(mask1, reinterpret_cast<T*>(voutput));
voutput += zcnt1;
if (zcnt2)
this->chunks[1].compress(mask2, reinterpret_cast<T*>(voutput));
voutput += zcnt2;
if (zcnt3)
this->chunks[2].compress(mask3, reinterpret_cast<T*>(voutput));
voutput += zcnt3;
if (zcnt4)
this->chunks[3].compress(mask4, reinterpret_cast<T*>(voutput));
voutput += zcnt4;
return reinterpret_cast<uint64_t>(voutput) - reinterpret_cast<uint64_t>(output);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline uint64_t to_bitmask() const {
__m128i mask1 = __lsx_vmskltz_b(this->chunks[0]);
__m128i mask2 = __lsx_vmskltz_b(this->chunks[1]);
__m128i mask3 = __lsx_vmskltz_b(this->chunks[2]);
__m128i mask4 = __lsx_vmskltz_b(this->chunks[3]);
mask1 = __lsx_vilvl_h(mask2, mask1);
mask2 = __lsx_vilvl_h(mask4, mask3);
return __lsx_vpickve2gr_du(__lsx_vilvl_w(mask2, mask1), 0);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_SIMD_H
/* end file simdjson/lsx/simd.h */
/* including simdjson/lsx/stringparsing_defs.h: #include "simdjson/lsx/stringparsing_defs.h" */
/* begin file simdjson/lsx/stringparsing_defs.h */
#ifndef SIMDJSON_LSX_STRINGPARSING_DEFS_H
#define SIMDJSON_LSX_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on LSX; therefore, we
// smash them together into a 64-byte mask and get the bitmask from there.
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_STRINGPARSING_DEFS_H
/* end file simdjson/lsx/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/lsx/begin.h */
/* including simdjson/generic/amalgamated.h for lsx: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for lsx */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for lsx: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for lsx */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for lsx */
/* including simdjson/generic/jsoncharutils.h for lsx: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for lsx */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for lsx */
/* including simdjson/generic/atomparsing.h for lsx: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for lsx */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace lsx {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for lsx */
/* including simdjson/generic/dom_parser_implementation.h for lsx: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for lsx */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace lsx
} // namespace simdjson
namespace simdjson {
namespace lsx {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for lsx */
/* including simdjson/generic/implementation_simdjson_result_base.h for lsx: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for lsx */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for lsx */
/* including simdjson/generic/numberparsing.h for lsx: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for lsx */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace lsx {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for lsx */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for lsx: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for lsx */
/* end file simdjson/generic/amalgamated.h for lsx */
/* including simdjson/lsx/end.h: #include "simdjson/lsx/end.h" */
/* begin file simdjson/lsx/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "lsx" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/lsx/end.h */
#endif // SIMDJSON_LSX_H
/* end file simdjson/lsx.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
/* including simdjson/rvv-vls.h: #include "simdjson/rvv-vls.h" */
/* begin file simdjson/rvv-vls.h */
#ifndef SIMDJSON_RVV_VLS_H
#define SIMDJSON_RVV_VLS_H
/* including simdjson/rvv-vls/begin.h: #include "simdjson/rvv-vls/begin.h" */
/* begin file simdjson/rvv-vls/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "rvv_vls" */
#define SIMDJSON_IMPLEMENTATION rvv_vls
/* including simdjson/rvv-vls/base.h: #include "simdjson/rvv-vls/base.h" */
/* begin file simdjson/rvv-vls/base.h */
#ifndef SIMDJSON_RVV_VLS_BASE_H
#define SIMDJSON_RVV_VLS_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* RVV-VLS implementation.
*/
namespace rvv_vls {
class implementation;
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BASE_H
/* end file simdjson/rvv-vls/base.h */
/* including simdjson/rvv-vls/intrinsics.h: #include "simdjson/rvv-vls/intrinsics.h" */
/* begin file simdjson/rvv-vls/intrinsics.h */
#ifndef SIMDJSON_RVV_VLS_INTRINSICS_H
#define SIMDJSON_RVV_VLS_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <riscv_vector.h>
#define simdutf_vrgather_u8m1x2(tbl, idx) \
__riscv_vcreate_v_u8m1_u8m2( \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 0), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 1), \
__riscv_vsetvlmax_e8m1()))
#define simdutf_vrgather_u8m1x4(tbl, idx) \
__riscv_vcreate_v_u8m1_u8m4( \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 0), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 1), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 2), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 3), \
__riscv_vsetvlmax_e8m1()))
#if __riscv_zbc
#include <riscv_bitmanip.h>
#endif
#endif // SIMDJSON_RVV_VLS_INTRINSICS_H
/* end file simdjson/rvv-vls/intrinsics.h */
/* including simdjson/rvv-vls/bitmanipulation.h: #include "simdjson/rvv-vls/bitmanipulation.h" */
/* begin file simdjson/rvv-vls/bitmanipulation.h */
#ifndef SIMDJSON_RVV_VLS_BITMANIPULATION_H
#define SIMDJSON_RVV_VLS_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
simdjson_inline long long int count_ones(uint64_t input_num) {
return __builtin_popcountll(input_num);
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BITMANIPULATION_H
/* end file simdjson/rvv-vls/bitmanipulation.h */
/* including simdjson/rvv-vls/bitmask.h: #include "simdjson/rvv-vls/bitmask.h" */
/* begin file simdjson/rvv-vls/bitmask.h */
#ifndef SIMDJSON_RVV_VLS_BITMASK_H
#define SIMDJSON_RVV_VLS_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
#if __riscv_zbc
return __riscv_clmul_64(bitmask, ~(uint64_t)0);
#elif __riscv_zvbc
return __riscv_vmv_x(__riscv_vclmul(__riscv_vmv_s_x_u64m1(bitmask, 1), ~(uint64_t)0, 1));
#else
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
#endif
return bitmask;
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BITMASK_H
/* end file simdjson/rvv-vls/bitmask.h */
/* including simdjson/rvv-vls/simd.h: #include "simdjson/rvv-vls/simd.h" */
/* begin file simdjson/rvv-vls/simd.h */
#ifndef SIMDJSON_RVV_VLS_SIMD_H
#define SIMDJSON_RVV_VLS_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
namespace simd {
#if __riscv_v_fixed_vlen >= 512
static constexpr size_t VL8 = 512/8;
using vint8_t = vint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vuint8_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vbool_t = vbool8_t __attribute__((riscv_rvv_vector_bits(512/8)));
using vbitmask_t = uint64_t;
#else
static constexpr size_t VL8 = __riscv_v_fixed_vlen/8;
using vint8_t = vint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
using vuint8_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
using vbool_t = vbool8_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen/8)));
#if __riscv_v_fixed_vlen == 128
using vbitmask_t = uint16_t;
#elif __riscv_v_fixed_vlen == 256
using vbitmask_t = uint32_t;
#endif
#endif
#if __riscv_v_fixed_vlen == 128
using vuint8x64_t = vuint8m4_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool2_t __attribute__((riscv_rvv_vector_bits(512/8)));
#elif __riscv_v_fixed_vlen == 256
using vuint8x64_t = vuint8m2_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool4_t __attribute__((riscv_rvv_vector_bits(512/8)));
#else
using vuint8x64_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool8_t __attribute__((riscv_rvv_vector_bits(512/8)));
#endif
template<typename T>
struct simd8;
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool> {
vbool_t value;
using bitmask_t = vbitmask_t;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vbool_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(__riscv_vmclr_m_b8(VL8)) {}
simdjson_inline simd8(bool _value) : simd8(splat(_value)) {}
simdjson_inline operator const vbool_t&() const { return value; }
simdjson_inline operator vbool_t&() { return value; }
static simdjson_inline simd8<bool> splat(bool _value) {
return __riscv_vreinterpret_b8(__riscv_vmv_v_x_u64m1(((uint64_t)!_value)-1, 1));
}
simdjson_inline vbitmask_t to_bitmask() const {
#if __riscv_v_fixed_vlen == 128
return __riscv_vmv_x(__riscv_vreinterpret_u16m1(value));
#elif __riscv_v_fixed_vlen == 256
return __riscv_vmv_x(__riscv_vreinterpret_u32m1(value));
#else
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(value));
#endif
}
// Bit operations
simdjson_inline simd8<bool> operator|(const simd8<bool> other) const { return __riscv_vmor(*this, other, VL8); }
simdjson_inline simd8<bool> operator&(const simd8<bool> other) const { return __riscv_vmand(*this, other, VL8); }
simdjson_inline simd8<bool> operator^(const simd8<bool> other) const { return __riscv_vmxor(*this, other, VL8); }
simdjson_inline simd8<bool> bit_andnot(const simd8<bool> other) const { return __riscv_vmandn(other, *this, VL8); }
simdjson_inline simd8<bool> operator~() const { return __riscv_vmnot(*this, VL8); }
simdjson_inline simd8<bool>& operator|=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline simd8<bool>& operator&=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline simd8<bool>& operator^=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Unsigned bytes
template<>
struct simd8<uint8_t> {
vuint8_t value;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vuint8_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(zero()) {}
simdjson_inline simd8(const uint8_t values[VL8]) : simd8(load(values)) {}
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdjson_inline simd8(simd8<bool> mask) : value(__riscv_vmerge_vxm_u8m1(zero(), -1, (vbool_t)mask, VL8)) {}
simdjson_inline operator const vuint8_t&() const { return this->value; }
simdjson_inline operator vuint8_t&() { return this->value; }
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(vuint8_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
static simdjson_inline vuint8_t splat(uint8_t _value) { return __riscv_vmv_v_x_u8m1(_value, VL8); }
static simdjson_inline vuint8_t zero() { return splat(0); }
static simdjson_inline vuint8_t load(const uint8_t values[VL8]) { return __riscv_vle8_v_u8m1(values, VL8); }
// Bit operations
simdjson_inline simd8<uint8_t> operator|(const simd8<uint8_t> other) const { return __riscv_vor_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator&(const simd8<uint8_t> other) const { return __riscv_vand_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator^(const simd8<uint8_t> other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator~() const { return __riscv_vnot_v_u8m1(value, VL8); }
#if __riscv_zvbb
simdjson_inline simd8<uint8_t> bit_andnot(const simd8<uint8_t> other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); }
#else
simdjson_inline simd8<uint8_t> bit_andnot(const simd8<uint8_t> other) const { return other & ~*this; }
#endif
simdjson_inline simd8<uint8_t>& operator|=(const simd8<uint8_t> other) { value = *this | other; return *this; }
simdjson_inline simd8<uint8_t>& operator&=(const simd8<uint8_t> other) { value = *this & other; return *this; }
simdjson_inline simd8<uint8_t>& operator^=(const simd8<uint8_t> other) { value = *this ^ other; return *this; }
simdjson_inline simd8<bool> operator==(const simd8<uint8_t> other) const { return __riscv_vmseq(value, other, VL8); }
simdjson_inline simd8<bool> operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); }
template<int N=1>
simdjson_inline simd8<uint8_t> prev(const simd8<uint8_t> prev_chunk) const {
return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8);
}
// Store to array
simdjson_inline void store(uint8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); }
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __riscv_vsaddu(value, other, VL8); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __riscv_vssubu(value, other, VL8); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<uint8_t> operator+(const simd8<uint8_t> other) const { return __riscv_vadd(value, other, VL8); }
simdjson_inline simd8<uint8_t> operator-(const simd8<uint8_t> other) const { return __riscv_vsub(value, other, VL8); }
simdjson_inline simd8<uint8_t>& operator+=(const simd8<uint8_t> other) { value = *this + other; return *this; }
simdjson_inline simd8<uint8_t>& operator-=(const simd8<uint8_t> other) { value = *this - other; return *this; }
// Order-specific operations
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return __riscv_vmsleu(value, other, VL8); }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return __riscv_vmsgeu(value, other, VL8); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return __riscv_vmsltu(value, other, VL8); }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return __riscv_vmsgtu(value, other, VL8); }
// Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero.
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this > other); }
// Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero.
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this < other); }
// Bit-specific operations
simdjson_inline bool any_bits_set_anywhere() const {
return __riscv_vfirst(__riscv_vmsne(value, 0, VL8), VL8) >= 0;
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return __riscv_vsrl(value, N, VL8); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return __riscv_vsll(value, N, VL8); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __riscv_vrgather(lookup_table, value, VL8);
}
// compress inactive elements, to match AVX-512 behavior
template<typename L>
simdjson_inline void compress(vbitmask_t mask, L * output) const {
mask = (vbitmask_t)~mask;
#if __riscv_v_fixed_vlen == 128
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u16m1(mask, 1));
#elif __riscv_v_fixed_vlen == 256
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u32m1(mask, 1));
#else
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u64m1(mask, 1));
#endif
__riscv_vse8_v_u8m1(output, __riscv_vcompress(value, m, VL8), count_ones(mask));
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> {
vint8_t value;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vint8_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(zero()) {}
simdjson_inline simd8(const int8_t values[VL8]) : simd8(load(values)) {}
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
simdjson_inline operator const vint8_t&() const { return this->value; }
simdjson_inline operator vint8_t&() { return this->value; }
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(vint8_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
static simdjson_inline vint8_t splat(int8_t _value) { return __riscv_vmv_v_x_i8m1(_value, VL8); }
static simdjson_inline vint8_t zero() { return splat(0); }
static simdjson_inline vint8_t load(const int8_t values[VL8]) { return __riscv_vle8_v_i8m1(values, VL8); }
simdjson_inline void store(int8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); }
// Explicit conversion to/from unsigned
simdjson_inline explicit simd8(const vuint8_t other): simd8(__riscv_vreinterpret_i8m1(other)) {}
simdjson_inline explicit operator simd8<uint8_t>() const { return __riscv_vreinterpret_u8m1(value); }
// Math
simdjson_inline simd8<int8_t> operator+(const simd8<int8_t> other) const { return __riscv_vadd(value, other, VL8); }
simdjson_inline simd8<int8_t> operator-(const simd8<int8_t> other) const { return __riscv_vsub(value, other, VL8); }
simdjson_inline simd8<int8_t>& operator+=(const simd8<int8_t> other) { value = *this + other; return *this; }
simdjson_inline simd8<int8_t>& operator-=(const simd8<int8_t> other) { value = *this - other; return *this; }
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val( const simd8<int8_t> other) const { return __riscv_vmax( value, other, VL8); }
simdjson_inline simd8<int8_t> min_val( const simd8<int8_t> other) const { return __riscv_vmin( value, other, VL8); }
simdjson_inline simd8<bool> operator>( const simd8<int8_t> other) const { return __riscv_vmsgt(value, other, VL8); }
simdjson_inline simd8<bool> operator<( const simd8<int8_t> other) const { return __riscv_vmslt(value, other, VL8); }
simdjson_inline simd8<bool> operator==(const simd8<int8_t> other) const { return __riscv_vmseq(value, other, VL8); }
template<int N=1>
simdjson_inline simd8<int8_t> prev(const simd8<int8_t> prev_chunk) const {
return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8);
}
// Perform a lookup assuming no value is larger than 16
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __riscv_vrgather(lookup_table, value, VL8);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
template<typename T>
struct simd8x64;
template<>
struct simd8x64<uint8_t> {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<uint8_t>);
vuint8x64_t value;
#if __riscv_v_fixed_vlen >= 512
template<int idx> simd8<uint8_t> get() const { return value; }
#else
template<int idx> simd8<uint8_t> get() const { return __riscv_vget_u8m1(value, idx); }
#endif
simdjson_inline operator const vuint8x64_t&() const { return this->value; }
simdjson_inline operator vuint8x64_t&() { return this->value; }
simd8x64(const simd8x64<uint8_t>& o) = delete; // no copy allowed
simd8x64<uint8_t>& operator=(const simd8<uint8_t>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
#if __riscv_v_fixed_vlen == 128
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m4(ptr, n)) {}
#elif __riscv_v_fixed_vlen == 256
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m2(ptr, n)) {}
#else
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m1(ptr, n)) {}
#endif
simdjson_inline void store(uint8_t ptr[64]) const {
__riscv_vse8(ptr, value, 64);
}
simdjson_inline bool is_ascii() const {
#if __riscv_v_fixed_vlen == 128
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m4(value), 0, 64), 64) < 0;
#elif __riscv_v_fixed_vlen == 256
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m2(value), 0, 64), 64) < 0;
#else
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m1(value), 0, 64), 64) < 0;
#endif
}
// compress inactive elements, to match AVX-512 behavior
simdjson_inline uint64_t compress(uint64_t mask, uint8_t * output) const {
mask = ~mask;
#if __riscv_v_fixed_vlen == 128
vboolx64_t m = __riscv_vreinterpret_b2(__riscv_vmv_s_x_u64m1(mask, 1));
#elif __riscv_v_fixed_vlen == 256
vboolx64_t m = __riscv_vreinterpret_b4(__riscv_vmv_s_x_u64m1(mask, 1));
#else
vboolx64_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u64m1(mask, 1));
#endif
size_t cnt = count_ones(mask);
__riscv_vse8(output, __riscv_vcompress(value, m, 64), cnt);
return cnt;
}
simdjson_inline uint64_t eq(const uint8_t m) const {
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vmseq(value, m, 64)));
}
simdjson_inline uint64_t lteq(const uint8_t m) const {
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vmsleu(value, m, 64)));
}
}; // struct simd8x64<uint8_t>
} // namespace simd
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_SIMD_H
/* end file simdjson/rvv-vls/simd.h */
/* including simdjson/rvv-vls/stringparsing_defs.h: #include "simdjson/rvv-vls/stringparsing_defs.h" */
/* begin file simdjson/rvv-vls/stringparsing_defs.h */
#ifndef SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
#define SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint64_t bs_bits;
uint64_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() };
}
struct escaping {
static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return { ((v == '"') | (v == '\\') | (v == 32)).to_bitmask() };
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
/* end file simdjson/rvv-vls/stringparsing_defs.h */
/* including simdjson/rvv-vls/numberparsing_defs.h: #include "simdjson/rvv-vls/numberparsing_defs.h" */
/* begin file simdjson/rvv-vls/numberparsing_defs.h */
#ifndef SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
#define SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#ifdef JSON_TEST_NUMBERS // for unit testing
void found_invalid_number(const uint8_t *buf);
void found_integer(int64_t result, const uint8_t *buf);
void found_unsigned_integer(uint64_t result, const uint8_t *buf);
void found_float(double result, const uint8_t *buf);
#endif
namespace simdjson {
namespace rvv_vls {
namespace numberparsing {
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) {
uint64_t val;
#if __riscv_misaligned_fast
memcpy(&val, chars, sizeof(uint64_t));
#else
val = __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vlmul_ext_u8m1(__riscv_vle8_v_u8mf2((uint8_t*)chars, 8))));
#endif
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars));
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace rvv_vls
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
/* end file simdjson/rvv-vls/numberparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/rvv-vls/begin.h */
/* including simdjson/generic/amalgamated.h for rvv_vls: #include "simdjson/generic/amalgamated.h" */
/* begin file simdjson/generic/amalgamated.h for rvv_vls */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_DEPENDENCIES_H)
#error simdjson/generic/dependencies.h must be included before simdjson/generic/amalgamated.h!
#endif
/* including simdjson/generic/base.h for rvv_vls: #include "simdjson/generic/base.h" */
/* begin file simdjson/generic/base.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): // If we haven't got an implementation yet, we're in the editor, editing a generic file! Just */
/* amalgamation skipped (editor-only): // use the most advanced one we can so the most possible stuff can be tested. */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #include "simdjson/implementation_detection.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_IMPLEMENTATION_ICELAKE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_HASWELL */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_WESTMERE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_ARM64 */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_PPC64 */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LASX */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_LSX */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_RVV_VLS */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/begin.h" */
/* amalgamation skipped (editor-only): #elif SIMDJSON_IMPLEMENTATION_FALLBACK */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/begin.h" */
/* amalgamation skipped (editor-only): #else */
/* amalgamation skipped (editor-only): #error "All possible implementations (including fallback) have been disabled! simdjson will not run." */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_IMPLEMENTATION */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
struct open_container;
class dom_parser_implementation;
/**
* The type of a JSON number
*/
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BASE_H
/* end file simdjson/generic/base.h for rvv_vls */
/* including simdjson/generic/jsoncharutils.h for rvv_vls: #include "simdjson/generic/jsoncharutils.h" */
/* begin file simdjson/generic/jsoncharutils.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_JSONCHARUTILS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_JSONCHARUTILS_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/jsoncharutils_tables.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
namespace jsoncharutils {
// return non-zero if not a structural or whitespace char
// zero otherwise
simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace_negated[c];
}
simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) {
return internal::structural_or_whitespace[c];
}
// returns a value with the high 16 bits set if not valid
// otherwise returns the conversion of the 4 hex digits at src into the bottom
// 16 bits of the 32-bit return register
//
// see
// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/
static inline uint32_t hex_to_u32_nocheck(
const uint8_t *src) { // strictly speaking, static inline is a C-ism
uint32_t v1 = internal::digit_to_val32[630 + src[0]];
uint32_t v2 = internal::digit_to_val32[420 + src[1]];
uint32_t v3 = internal::digit_to_val32[210 + src[2]];
uint32_t v4 = internal::digit_to_val32[0 + src[3]];
return v1 | v2 | v3 | v4;
}
// given a code point cp, writes to c
// the utf-8 code, outputting the length in
// bytes, if the length is zero, the code point
// is invalid
//
// This can possibly be made faster using pdep
// and clz and table lookups, but JSON documents
// have few escaped code points, and the following
// function looks cheap.
//
// Note: we assume that surrogates are treated separately
//
simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) {
if (cp <= 0x7F) {
c[0] = uint8_t(cp);
return 1; // ascii
}
if (cp <= 0x7FF) {
c[0] = uint8_t((cp >> 6) + 192);
c[1] = uint8_t((cp & 63) + 128);
return 2; // universal plane
// Surrogates are treated elsewhere...
//} //else if (0xd800 <= cp && cp <= 0xdfff) {
// return 0; // surrogates // could put assert here
} else if (cp <= 0xFFFF) {
c[0] = uint8_t((cp >> 12) + 224);
c[1] = uint8_t(((cp >> 6) & 63) + 128);
c[2] = uint8_t((cp & 63) + 128);
return 3;
} else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this
// is not needed
c[0] = uint8_t((cp >> 18) + 240);
c[1] = uint8_t(((cp >> 12) & 63) + 128);
c[2] = uint8_t(((cp >> 6) & 63) + 128);
c[3] = uint8_t((cp & 63) + 128);
return 4;
}
// will return 0 when the code point was too large.
return 0; // bad r
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
} // namespace jsoncharutils
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_JSONCHARUTILS_H
/* end file simdjson/generic/jsoncharutils.h for rvv_vls */
/* including simdjson/generic/atomparsing.h for rvv_vls: #include "simdjson/generic/atomparsing.h" */
/* begin file simdjson/generic/atomparsing.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ATOMPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ATOMPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace rvv_vls {
namespace {
/// @private
namespace atomparsing {
// The string_to_uint32 is exclusively used to map literal strings to 32-bit values.
// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot
// be certain that the character pointer will be properly aligned.
// You might think that using memcpy makes this function expensive, but you'd be wrong.
// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false");
// to the compile-time constant 1936482662.
simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; }
// Acts on the same principle as string_to_uint32, but on an 8-byte block of memory
simdjson_inline uint64_t string_to_uint64(const char* str) { uint64_t val; std::memcpy(&val, str, sizeof(uint64_t)); return val; }
// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive.
// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about.
simdjson_warn_unused
simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) {
uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes");
std::memcpy(&srcval, src, sizeof(uint32_t));
return srcval ^ string_to_uint32(atom);
}
// Checks that the first 8 characters of the input string match the given atom in a case-insensitive manner.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint64_t str8ncmp_case_insensitive(const uint8_t *src, const char* atom) {
uint64_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++)
static_assert(sizeof(uint64_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 8 bytes");
std::memcpy(&srcval, src, sizeof(uint64_t));
return (srcval | 0x2020202020202020ull) ^ string_to_uint64(atom);
}
// Checks that the first 3 characters of 'src' match 'atom' in a case-insensitive way.
//
// 'atom' must consist of only lowercase letters.
simdjson_warn_unused
simdjson_inline uint32_t str3ncmp_case_insensitive(const uint8_t *src, const char* atom) {
return ((src[0] | 0x20) ^ atom[0]) //
| ((src[1] | 0x20) ^ atom[1]) //
| ((src[2] | 0x20) ^ atom[2]);
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src) {
return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_true_atom(src); }
else if (len == 4) { return !str4ncmp(src, "true"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src) {
return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) {
if (len > 5) { return is_valid_false_atom(src); }
else if (len == 5) { return !str4ncmp(src+1, "alse"); }
else { return false; }
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src) {
return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) {
if (len > 4) { return is_valid_null_atom(src); }
else if (len == 4) { return !str4ncmp(src, "null"); }
else { return false; }
}
#if SIMDJSON_ENABLE_NAN_INF
// "nan" is 3 bytes; we check characters and then verify the next
// character is structural or whitespace. We accept both "nan" and "NaN".
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
// checks that the next four characters of a string are 'nan"', where the 'nan'
// is checked in a case-insensitive way.
simdjson_warn_unused
simdjson_inline bool is_valid_nan_in_string(const uint8_t *src) {
return (str3ncmp_case_insensitive(src, "nan") | (src[3] ^ '"')) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_nan_atom(const uint8_t *src, size_t len) {
if (len > 3) { return is_valid_nan_atom(src); }
if (len == 3) { return str3ncmp_case_insensitive(src, "nan") == 0; }
return false;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
if(is_short_inf) return true;
// Check for 'infinity' (any capitalization)
return (str8ncmp_case_insensitive(src, "infinity") | jsoncharutils::is_not_structural_or_whitespace(src[8])) == 0;
}
simdjson_warn_unused
simdjson_inline bool is_valid_inf_in_string(const uint8_t *src) {
bool is_short_inf = (str3ncmp_case_insensitive(src, "inf") | (src[3] ^ '"')) == 0;
if(is_short_inf) return true;
return (str8ncmp_case_insensitive(src, "infinity") | (src[8] ^ '"')) == 0;
}
// This function will accept any case-insensitive 3-character spelling of
// infinity: 'inf', 'INF', and 'Inf' are all accepted.
//
// Any capitalization of 'infinity' is also accepted.
simdjson_warn_unused
simdjson_inline bool is_valid_inf_atom(const uint8_t *src, size_t len) {
if (len > 8) { return is_valid_inf_atom(src); }
if (len == 8 && str8ncmp_case_insensitive(src, "infinity") == 0) {
return true;
}
if (len > 3) {
return (str3ncmp_case_insensitive(src, "inf")
| jsoncharutils::is_not_structural_or_whitespace(src[3])) == 0;
}
if (len == 3) { return str3ncmp_case_insensitive(src, "inf") == 0; }
return false;
}
#endif // SIMDJSON_ENABLE_NAN_INF
} // namespace atomparsing
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ATOMPARSING_H
/* end file simdjson/generic/atomparsing.h for rvv_vls */
/* including simdjson/generic/dom_parser_implementation.h for rvv_vls: #include "simdjson/generic/dom_parser_implementation.h" */
/* begin file simdjson/generic/dom_parser_implementation.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
// expectation: sizeof(open_container) = 64/8.
struct open_container {
uint32_t tape_index; // where, on the tape, does the scope ([,{) begins
uint32_t count; // how many elements in the scope
}; // struct open_container
static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits");
class dom_parser_implementation final : public internal::dom_parser_implementation {
public:
/** Tape location of each open { or [ */
std::unique_ptr<open_container[]> open_containers{};
/** Whether each open container is a [ or { */
std::unique_ptr<bool[]> is_array{};
/** Buffer passed to stage 1 */
const uint8_t *buf{};
/** Length passed to stage 1 */
size_t len{0};
/** Document passed to stage 2 */
dom::document *doc{};
inline dom_parser_implementation() noexcept;
inline dom_parser_implementation(dom_parser_implementation &&other) noexcept;
inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept;
dom_parser_implementation(const dom_parser_implementation &) = delete;
dom_parser_implementation &operator=(const dom_parser_implementation &) = delete;
simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final;
simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final;
simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final;
simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept final;
simdjson_warn_unused uint8_t *parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept final;
inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final;
inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final;
private:
simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity);
};
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
namespace rvv_vls {
inline dom_parser_implementation::dom_parser_implementation() noexcept = default;
inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default;
inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default;
// Leaving these here so they can be inlined if so desired
inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept {
if(capacity > SIMDJSON_MAXSIZE_BYTES || capacity > SIZE_MAX - 63) { return CAPACITY; }
// Stage 1 index output
size_t rounded_capacity = SIMDJSON_ROUNDUP_N(capacity, 64);
if(rounded_capacity + 9 < rounded_capacity) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t max_structures = rounded_capacity + 9;
if(max_structures > SIZE_MAX / sizeof(uint32_t)) { return CAPACITY; }
structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] );
if (!structural_indexes) { _capacity = 0; return MEMALLOC; }
structural_indexes[0] = 0;
n_structural_indexes = 0;
_capacity = capacity;
return SUCCESS;
}
inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept {
if(max_depth == 0 || max_depth > SIMDJSON_MAX_DEPTH || max_depth > SIZE_MAX / sizeof(open_container)) { return CAPACITY; }
// Stage 2 stacks
open_containers.reset(new (std::nothrow) open_container[max_depth]);
is_array.reset(new (std::nothrow) bool[max_depth]);
if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; }
_max_depth = max_depth;
return SUCCESS;
}
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_DOM_PARSER_IMPLEMENTATION_H
/* end file simdjson/generic/dom_parser_implementation.h for rvv_vls */
/* including simdjson/generic/implementation_simdjson_result_base.h for rvv_vls: #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair
// so we can avoid inlining errors
// TODO reconcile these!
/**
* The result of a simdjson operation that could fail.
*
* Gives the option of reading error codes, or throwing an exception by casting to the desired result.
*
* This is a base class for implementations that want to add functions to the result type for
* chaining.
*
* Override like:
*
* struct simdjson_result<T> : public internal::implementation_simdjson_result_base<T> {
* simdjson_result() noexcept : internal::implementation_simdjson_result_base<T>() {}
* simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base<T>(error) {}
* simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base<T>(std::forward(value)) {}
* simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base<T>(value, error) {}
* // Your extra methods here
* }
*
* Then any method returning simdjson_result<T> will be chainable with your methods.
*/
template<typename T>
struct implementation_simdjson_result_base {
/**
* Create a new empty result with error = UNINITIALIZED.
*/
simdjson_inline implementation_simdjson_result_base() noexcept = default;
/**
* Create a new error result.
*/
simdjson_inline implementation_simdjson_result_base(error_code error) noexcept;
/**
* Create a new successful result.
*/
simdjson_inline implementation_simdjson_result_base(T &&value) noexcept;
/**
* Create a new result with both things (use if you don't want to branch when creating the result).
*/
simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept;
/**
* Move the value and the error to the provided variables.
*
* @param value The variable to assign the value to. May not be set if there is an error.
* @param error The variable to assign the error to. Set to SUCCESS if there is no error.
*/
simdjson_inline void tie(T &value, error_code &error) && noexcept;
/**
* Move the value to the provided variable.
*
* @param value The variable to assign the value to. May not be set if there is an error.
*/
simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept;
/**
* The error.
*/
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
/**
* Whether there is a value.
*/
simdjson_warn_unused simdjson_inline bool has_value() const noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Get the result value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T& operator*() & noexcept(false);
simdjson_inline T&& operator*() && noexcept(false);
/**
* Arrow operator to access members of the contained value.
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T* operator->() noexcept(false);
simdjson_inline const T* operator->() const noexcept(false);
simdjson_inline T& value() & noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& value() && noexcept(false);
/**
* Take the result value (move it).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline T&& take_value() && noexcept(false);
/**
* Cast to the value (will throw on error).
*
* @throw simdjson_error if there was an error.
*/
simdjson_inline operator T&&() && noexcept(false);
#endif // SIMDJSON_EXCEPTIONS
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline const T& value_unsafe() const& noexcept;
/**
* Get the result value. This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T& value_unsafe() & noexcept;
/**
* Take the result value (move it). This function is safe if and only
* the error() method returns a value that evaluates to false.
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/
}; // struct implementation_simdjson_result_base
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_H
/* end file simdjson/generic/implementation_simdjson_result_base.h for rvv_vls */
/* including simdjson/generic/numberparsing.h for rvv_vls: #include "simdjson/generic/numberparsing.h" */
/* begin file simdjson/generic/numberparsing.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_NUMBERPARSING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_NUMBERPARSING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/jsoncharutils.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
#include <ostream>
#include <cstring>
namespace simdjson {
namespace rvv_vls {
namespace numberparsing {
#ifdef JSON_TEST_NUMBERS
#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE)))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE)))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE)))
#define BIGINT_NUMBER(SRC) (found_invalid_number((SRC)), BIGINT_ERROR)
#else
#define INVALID_NUMBER(SRC) (NUMBER_ERROR)
#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE))
#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE))
#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE))
#define BIGINT_NUMBER(SRC) (BIGINT_ERROR)
#endif
namespace {
// Convert a mantissa, an exponent and a sign bit into an ieee64 double.
// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable).
// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed.
simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) {
double d;
mantissa &= ~(1ULL << 52);
mantissa |= real_exponent << 52;
mantissa |= ((static_cast<uint64_t>(negative)) << 63);
std::memcpy(&d, &mantissa, sizeof(d));
return d;
}
// Convert a mantissa, an exponent and a sign bit into an ieee32 float (binary32).
// The real_exponent needs to be in [0, 254] (technically real_exponent = 255 would be acceptable).
// The mantissa should be in [0,1<<24). The bit at index (1U << 23) will be zeroed.
simdjson_inline float to_float(uint32_t mantissa, uint32_t real_exponent, bool negative) {
float f;
mantissa &= ~(uint32_t(1) << 23);
mantissa |= real_exponent << 23;
mantissa |= ((static_cast<uint32_t>(negative)) << 31);
std::memcpy(&f, &mantissa, sizeof(f));
return f;
}
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// This function will only work in some cases, when it does not work, success is
// set to false. This should work *most of the time* (like 99% of the time).
// We assume that power is in the [smallest_power,
// largest_power] interval: the caller is responsible for this check.
simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) {
// we start with a fast path
// It was described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 22 && i <= 9007199254740991)
#else
if (-22 <= power && power <= 22 && i <= 9007199254740991)
#endif
{
// convert the integer into a double. This is lossless since
// 0 <= i <= 2^53 - 1.
d = double(i);
//
// The general idea is as follows.
// If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then
// 1) Both s and p can be represented exactly as 64-bit floating-point
// values
// (binary64).
// 2) Because s and p can be represented exactly as floating-point values,
// then s * p
// and s / p will produce correctly rounded values.
//
if (power < 0) {
d = d / simdjson::internal::power_of_ten[-power];
} else {
d = d * simdjson::internal::power_of_ten[power];
}
if (negative) {
d = -d;
}
return true;
}
// When 22 < power && power < 22 + 16, we could
// hope for another, secondary fast path. It was
// described by David M. Gay in "Correctly rounded
// binary-decimal and decimal-binary conversions." (1990)
// If you need to compute i * 10^(22 + x) for x < 16,
// first compute i * 10^x, if you know that result is exact
// (e.g., when i * 10^x < 2^53),
// then you can still proceed and do (i * 10^x) * 10^22.
// Is this worth your time?
// You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53)
// for this second fast path to work.
// If you you have 22 < power *and* power < 22 + 16, and then you
// optimistically compute "i * 10^(x-22)", there is still a chance that you
// have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of
// this optimization maybe less common than we would like. Source:
// http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
// also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html
// The fast path has now failed, so we are failing back on the slower path.
// In the slow path, we need to adjust i so that it is > 1<<63 which is always
// possible, except if i == 0, so we handle i == 0 separately.
if(i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
// The exponent is 1024 + 63 + power
// + floor(log(5**power)/log(2)).
// The 1024 comes from the ieee64 standard.
// The 63 comes from the fact that we use a 64-bit word.
//
// Computing floor(log(5**power)/log(2)) could be
// slow. Instead we use a fast function.
//
// For power in (-400,350), we have that
// (((152170 + 65536) * power ) >> 16);
// is equal to
// floor(log(5**power)/log(2)) + power when power >= 0
// and it is equal to
// ceil(log(5**-power)/log(2)) + power when power < 0
//
// The 65536 is (1<<16) and corresponds to
// (65536 * power) >> 16 ---> power
//
// ((152170 * power ) >> 16) is equal to
// floor(log(5**power)/log(2))
//
// Note that this is not magic: 152170/(1<<16) is
// approximately equal to log(5)/log(2).
// The 1<<16 value is a power of two; we could use a
// larger power of 2 if we wanted to.
//
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We are going to need to do some 64-bit arithmetic to get a precise product.
// We use a table lookup approach.
// It is safe because
// power >= smallest_power
// and power <= largest_power
// We recover the mantissa of the power, it has a leading 1. It is always
// rounded down.
//
// We want the most significant 64 bits of the product. We know
// this will be non-zero because the most significant bit of i is
// 1.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
// Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.)
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Both i and power_of_five_128[index] have their most significant bit set to 1 which
// implies that the either the most or the second most significant bit of the product
// is 1. We pack values in this manner for efficiency reasons: it maximizes the use
// we make of the product. It also makes it easy to reason about the product: there
// is 0 or 1 leading zero in the product.
// Unless the least significant 9 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 55 bits are
// exact and no further work is needed. Having 55 bits is necessary because
// we need 53 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
if((firstproduct.high & 0x1FF) == 0x1FF) {
// We want to compute i * 5^q, but only care about the top 55 bits at most.
// Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing
// the full computation is wasteful. So we do what is called a "truncated
// multiplication".
// We take the most significant 64-bits, and we put them in
// power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q
// to the desired approximation using one multiplication. Sometimes it does not suffice.
// Then we store the next most significant 64 bits in power_of_five_128[index + 1], and
// then we get a better approximation to i * 5^q.
//
// That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat
// more complicated.
//
// There is an extra layer of complexity in that we need more than 55 bits of
// accuracy in the round-to-even scenario.
//
// The full_multiplication function computes the 128-bit product of two 64-bit words
// with a returned value of type value128 with a "low component" corresponding to the
// 64-bit least significant bits of the product and with a "high component" corresponding
// to the 64-bit most significant bits of the product.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
// As it has been proven by Noble Mushtak and Daniel Lemire in "Fast Number Parsing Without
// Fallback" (https://arxiv.org/abs/2212.06644), at this point we are sure that the product
// is sufficiently accurate, and more computation is not needed.
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 53 bits with a leading 1.
// We shift it so that it occupies 54 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<54).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0 : 0.0;
return true;
}
// next line is safe because -real_exponent + 1 < 0
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just.
// Suppose we start with 2.2250738585072013e-308, we end up
// with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
// whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
// up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
// subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold.
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
// We have to round to even. The "to even" part
// is only a problem when we are right in between two floats
// which we guard against.
// If we have lots of trailing zeros, we may fall right between two
// floating-point values.
//
// The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54]
// times a power of two. That is, it is right between a number with binary significand
// m and another number with binary significand m+1; and it must be the case
// that it cannot be represented by a float itself.
//
// We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p.
// Recall that 10^q = 5^q * 2^q.
// When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that
// 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23.
// When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so
// (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have
// 2^{53} x 5^{-q} < 2^{64}.
// Hence we have 5^{-q} < 2^{11}$ or q>= -4.
//
// We require lower <= 1 and not lower == 0 because we could not prove that
// that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test.
if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1; // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<53), unless there was an overflow
if (mantissa >= (1ULL << 53)) {
//////////
// This will happen when parsing values such as 7.2057594037927933e+16
////////
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 2046)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Attempts to compute i * 10^(power) as a binary32 (float) value; and if
// "negative" is true, negate the result.
//
// This is the same Eisel-Lemire algorithm as compute_float_64, retargeted at
// binary32. It is adapted from fast_float
// (https://github.com/fastfloat/fast_float), where the routine is written once
// and instantiated for each binary format. We only need 24 bits of mantissa
// instead of 53, so we ask the truncated product for 24 + 2 = 26 accurate bits
// (plus one bit that may be lost to the "upperbit" shift), and we shift the
// product right by (upperbit + 64 - 23 - 3) instead of (upperbit + 64 - 52 - 3).
//
// The power-of-five table is shared with the binary64 code: the useful range
// for binary32, [smallest_power_binary32, largest_power_binary32], is a strict
// subset of [smallest_power, largest_power].
//
// The function returns false when the result would be infinite: simdjson
// refuses to parse infinite values, so the caller reports an error. Unlike
// compute_float_64, a false return never means "try harder": the accuracy of
// the two-limb product is guaranteed by Noble Mushtak and Daniel Lemire,
// "Fast Number Parsing Without Fallback" (https://arxiv.org/abs/2212.06644).
// The caller must still fall back to a slow path when the 64-bit mantissa i
// was truncated (more than 19 significant digits).
//
// We assume that power is in the [smallest_power, largest_power] interval: the
// caller is responsible for this check.
simdjson_inline bool compute_float_32(int64_t power, uint64_t i, bool negative, float &d) {
// Powers of ten that are exactly representable as binary32 values: 10^k is
// exact as long as k <= 10 (5^10 = 9765625 < 2**24 while 5^11 > 2**24).
static constexpr float power_of_ten_float[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// The range of powers of ten that a non-zero, finite binary32 value can be
// built from. Anything smaller rounds to zero, anything larger is infinite.
// These are the smallest_power_of_ten()/largest_power_of_ten() constants that
// fast_float uses for binary32.
constexpr int smallest_power_binary32 = -65;
constexpr int largest_power_binary32 = 38;
// We start with the fast path described in
// Clinger WD. How to read floating point numbers accurately.
// ACM SIGPLAN Notices. 1990
#ifndef FLT_EVAL_METHOD
#error "FLT_EVAL_METHOD should be defined, please include cfloat."
#endif
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
// We cannot be certain that x/y is rounded to nearest.
if (0 <= power && power <= 10 && i <= 16777215)
#else
if (-10 <= power && power <= 10 && i <= 16777215)
#endif
{
// Convert the integer into a float. This is lossless since
// 0 <= i <= 2^24 - 1.
d = float(i);
// Both d and the power of ten are exactly representable as binary32
// values, so the product (or quotient) is correctly rounded.
if (power < 0) {
d = d / power_of_ten_float[-power];
} else {
d = d * power_of_ten_float[power];
}
if (negative) {
d = -d;
}
return true;
}
// The fast path has failed, so we fall back on the Eisel-Lemire algorithm.
// It needs i > 0 (so that the leading bit of i can be normalized), so we
// handle i == 0 separately. We also handle the powers of ten that are so
// small (or so large) that the answer is zero (or infinite) whatever the
// mantissa is.
if (i == 0 || power < smallest_power_binary32) {
d = negative ? -0.0f : 0.0f;
return true;
}
if (power > largest_power_binary32) {
// We have, for sure, an infinite value.
return false;
}
// The exponent is 128 + 63 + power + floor(log(5**power)/log(2)).
// The 128 comes from the ieee32 standard (the minimal exponent is -127).
// The 63 comes from the fact that we use a 64-bit word.
// See compute_float_64 for a discussion of the magical 152170 + 65536.
int64_t exponent = (((152170 + 65536) * power) >> 16) + 128 + 63;
// We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(i);
i <<= lz;
// We want the most significant 64 bits of the product i * 5**power. It is
// safe to index the table because
// smallest_power <= smallest_power_binary32 <= power
// <= largest_power_binary32 <= largest_power.
const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power);
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index]);
#else
simdjson::internal::value128 firstproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index]);
#endif
// Unless the least significant 38 bits of the high (64-bit) part of the full
// product are all 1s, then we know that the most significant 26 bits are
// exact and no further work is needed. Having 26 bits is necessary because
// we need 24 bits for the mantissa but we have to have one rounding bit and
// we can waste a bit if the most significant bit of the product is zero.
// (For binary64, the same reasoning gives 55 bits and a 9-bit mask.)
if((firstproduct.high & 0x3FFFFFFFFF) == 0x3FFFFFFFFF) {
// The truncated multiplication was not accurate enough; use the next 64
// bits of the power of five to refine it. See compute_float_64 for a
// detailed discussion.
#if SIMDJSON_STATIC_REFLECTION
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::powers_template<>::power_of_five_128[index + 1]);
#else
simdjson::internal::value128 secondproduct = full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]);
#endif
firstproduct.low += secondproduct.high;
if(secondproduct.high > firstproduct.low) { firstproduct.high++; }
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
// The final mantissa should be 24 bits with a leading 1.
// We shift it so that it occupies 25 bits with a leading 1.
///////
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 38); // 38 == 64 - 23 - 3
lz += int(1 ^ upperbit);
// Here we have mantissa < (1<<25).
int64_t real_exponent = exponent - lz;
if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal?
// Here we have that real_exponent <= 0 so -real_exponent >= 0
if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
d = negative ? -0.0f : 0.0f;
return true;
}
// next line is safe because -real_exponent + 1 < 64
mantissa >>= -real_exponent + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0.
mantissa += (mantissa & 1); // round up
mantissa >>= 1;
// As in compute_float_64, rounding up may take us out of the subnormal
// range, so we can only decide after rounding.
real_exponent = (mantissa < (uint64_t(1) << 23)) ? 0 : 1;
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
// We have to round to even. The "to even" part is only a problem when we are
// right in between two floats, which we guard against. The bounds on the
// power of ten are those of fast_float's
// min_exponent_round_to_even()/max_exponent_round_to_even() for binary32:
// when q >= 0, (2m+1) must be divisible by 5^q with 5^q <= 2^25, so q <= 10;
// when q < 0, we need 2^24 x 5^{-q} < 2^{64}, so q >= -17.
if (simdjson_unlikely((lower <= 1) && (power >= -17) && (power <= 10) && ((mantissa & 3) == 1))) {
if((mantissa << (upperbit + 38)) == upper) {
mantissa &= ~uint64_t(1); // flip it so that we do not round up
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
// Here we have mantissa < (1<<24), unless there was an overflow
if (mantissa >= (uint64_t(1) << 24)) {
mantissa = (uint64_t(1) << 23);
real_exponent++;
}
mantissa &= ~(uint64_t(1) << 23);
// we have to check that real_exponent is in range, otherwise we bail out
if (simdjson_unlikely(real_exponent > 254)) {
// We have an infinite value!!! We could actually throw an error here if we could.
return false;
}
d = to_float(uint32_t(mantissa), uint32_t(real_exponent), negative);
return true;
}
#if SIMDJSON_ENABLE_NAN_INF
// Parses a nan or infinity. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, double& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
double inf = std::numeric_limits<double>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<double>::quiet_NaN();
return true;
}
return false;
}
// Parses a nan or infinity as a binary32 value. Returns true on success, false on failure.
simdjson_unused simdjson_inline bool compute_nan_inf(const uint8_t* src, bool negative, float& d) noexcept {
if (atomparsing::is_valid_inf_atom(src)) {
float inf = std::numeric_limits<float>::infinity();
d = negative ? -inf : inf;
return true;
}
if (atomparsing::is_valid_nan_atom(src)) {
d = std::numeric_limits<float>::quiet_NaN();
return true;
}
return false;
}
#endif
// We call a fallback floating-point parser that might be slow. Note
// it will accept JSON numbers, but the JSON spec. is more restrictive so
// before you call parse_float_fallback, you need to have validated the input
// string with the JSON grammar.
// It will return an error (false) if the parsed number is infinite.
// The string parsing itself always succeeds. We know that there is at least
// one digit.
static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) {
*outDouble = simdjson::internal::from_chars(reinterpret_cast<const char *>(ptr), reinterpret_cast<const char *>(end_ptr));
// We do not accept infinite values.
// Detecting finite values in a portable manner is ridiculously hard, ideally
// we would want to do:
// return !std::isfinite(*outDouble);
// but that mysteriously fails under legacy/old libc++ libraries, see
// https://github.com/simdjson/simdjson/issues/1286
//
// Therefore, fall back to this solution (the extra parens are there
// to handle that max may be a macro on windows).
return !(*outDouble > (std::numeric_limits<double>::max)() || *outDouble < std::numeric_limits<double>::lowest());
}
// Same as parse_float_fallback, but for binary32 (float) values. Going through
// the binary64 fallback and then rounding to binary32 would be subject to
// double rounding, so we run the fallback algorithm directly on binary32.
static bool parse_float_fallback(const uint8_t *ptr, float *outFloat) {
*outFloat = simdjson::internal::from_chars_float(reinterpret_cast<const char *>(ptr));
// We do not accept infinite values. See the binary64 version above for why we
// do not use std::isfinite.
return !(*outFloat > (std::numeric_limits<float>::max)() || *outFloat < std::numeric_limits<float>::lowest());
}
// check quickly whether the next 8 chars are made of digits
// at a glance, it looks better than Mula's
// http://0x80.pl/articles/swar-digits-validate.html
simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) {
uint64_t val;
// this can read up to 7 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7");
std::memcpy(&val, chars, 8);
// a branchy method might be faster:
// return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030)
// && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) ==
// 0x3030303030303030);
return (((val & 0xF0F0F0F0F0F0F0F0) |
(((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) ==
0x3333333333333333);
}
// The same idea for four characters. A block of eight only bites when eight
// digits are left, so a run of 4 to 7 digits used to fall back to one digit at a
// time; taking four of them at once is what makes the tail of a long fraction
// cheap. Adapted from fast_float, credit @aqrit.
simdjson_inline bool is_made_of_four_digits_fast(const uint8_t *chars) {
uint32_t val;
// Reads up to 3 bytes beyond the digits, which SIMDJSON_PADDING covers.
static_assert(3 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 3");
std::memcpy(&val, chars, 4);
return !(((val + 0x46464646) | (val - 0x30303030)) & 0x80808080);
}
// Only call this when is_made_of_four_digits_fast() says the four characters are
// digits. Little-endian: guarded by SIMDJSON_SWAR_NUMBER_PARSING.
simdjson_inline uint32_t parse_four_digits_unrolled(const uint8_t *chars) {
uint32_t val;
std::memcpy(&val, chars, 4);
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
template<typename I>
SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later
simdjson_inline bool parse_digit(const uint8_t c, I &i) {
const uint8_t digit = static_cast<uint8_t>(c - '0');
if (digit > 9) {
return false;
}
// PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication
i = 10 * i + digit; // might overflow, we will handle the overflow later
return true;
}
simdjson_inline bool is_digit(const uint8_t c) {
return static_cast<uint8_t>(c - '0') <= 9;
}
// Consumes a run of digits into i, eight at a time while eight are available,
// then four, then one at a time. Overflow is deliberate: the caller counts the
// digits and falls back when there are too many for a 64-bit mantissa.
//
// Long runs of digits are what a float-heavy document is made of: the fraction
// of a binary64 printed to full precision is 15 to 17 digits, and reading those
// one at a time is the single largest cost in parse_double(). Integer parts are
// left alone: they are usually a handful of digits, and there the check for a
// block of eight is wasted work.
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_fraction_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
while (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
// A 4 to 7 digit remainder is the common case once the blocks of eight are
// gone: 15 fraction digits leave 7, and 17 leave 1 after two blocks.
if (is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_float_integer_digits(const uint8_t *&p, uint64_t &i,
bool &leading_zero) {
if (!parse_digit(*p, i)) { leading_zero = true; return; }
p++;
leading_zero = (i == 0);
while (parse_digit(*p, i)) { p++; }
}
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline void parse_integer_digits(const uint8_t *&p, uint64_t &i) {
#ifdef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_SWAR_NUMBER_PARSING
// Identifiers, timestamps and counters often have eight digits or more.
// Prior related work: jsonifier parses integers as eight-digit SWAR words
// (str_to_i.hpp, https://github.com/nihilai-collective/Jsonifier). This
// takes one such word with parse_eight_digits_unrolled, the routine
// simdjson uses for long fractions.
if (is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p);
p += 8;
}
const uint8_t *const swar_end = p + 8;
while (p < swar_end && is_made_of_four_digits_fast(p)) {
i = i * 10000 + parse_four_digits_unrolled(p);
p += 4;
}
#endif // SIMDJSON_SWAR_NUMBER_PARSING
#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING
while (parse_digit(*p, i)) { p++; }
}
simdjson_warn_unused simdjson_inline error_code parse_decimal_after_separator(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) {
// we continue with the fiction that we have an integer. If the
// floating point number is representable as x * 10^z for some integer
// z that fits in 53 bits, then we will be able to convert back the
// the integer into a float in a lossless manner.
const uint8_t *const first_after_period = p;
parse_fraction_digits(p, i);
exponent = first_after_period - p;
// Decimal without digits (123.) is illegal
if (exponent == 0) {
return INVALID_NUMBER(src);
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) {
// Exp Sign: -123.456e[-]78
bool neg_exp = ('-' == *p);
if (neg_exp || '+' == *p) { p++; } // Skip + as well
// Exponent: -123.456e-[78]
auto start_exp = p;
int64_t exp_number = 0;
while (parse_digit(*p, exp_number)) { ++p; }
// It is possible for parse_digit to overflow.
// In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN.
// Thus we *must* check for possible overflow before we negate exp_number.
// Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into
// a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may
// not oblige and may, in fact, generate two distinct paths in any case. It might be
// possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off
// instructions for a simdjson_likely branch, an unconclusive gain.
// If there were no digits, it's an error.
if (simdjson_unlikely(p == start_exp)) {
return INVALID_NUMBER(src);
}
// We have a valid positive exponent in exp_number at this point, except that
// it may have overflowed.
// 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 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
// than 999,999,999,999,999,999.
// We can truncate.
// Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before
// infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could
// truncate at 324.
// Note that there is no reason to fail per se at this point in time.
// E.g., 0e999999999999999999999 is a fine number.
if (p > start_exp+18) { exp_number = 999999999999999999; }
}
// At this point, we know that exp_number is a sane, positive, signed integer.
// It is <= 999,999,999,999,999,999. As long as 'exponent' is in
// [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent'
// is bounded in magnitude by the size of the JSON input, we are fine in this universe.
// To sum it up: the next line should never overflow.
exponent += (neg_exp ? -exp_number : exp_number);
return SUCCESS;
}
simdjson_inline bool check_if_integer(const uint8_t *const src, size_t max_length) {
const uint8_t *const srcend = src + max_length;
bool negative = (*src == '-'); // we can always read at least one character after the '-'
const uint8_t *p = src + uint8_t(negative);
if(p == srcend) { return false; }
if(*p == '0') {
++p;
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
while(p != srcend && is_digit(*p)) { ++p; }
if(p == srcend) { return true; }
if(jsoncharutils::is_not_structural_or_whitespace(*p)) { return false; }
return true;
}
simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) {
// It is possible that the integer had an overflow.
// We have to handle the case where we have 0.0000somenumber.
const uint8_t *start = start_digits;
while ((*start == '0') || (*start == '.')) { ++start; }
// we over-decrement by one when there is a '.'
return digit_count - size_t(start - start_digits);
}
} // unnamed namespace
/** @private */
inline error_code slow_float_parsing(simdjson_unused const uint8_t * src, double* answer) {
if (parse_float_fallback(src, answer)) {
return SUCCESS;
}
return INVALID_NUMBER(src);
}
/** @private */
template<typename W>
simdjson_warn_unused simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) {
// If we frequently had to deal with long strings of digits,
// we could extend our code by using a 128-bit integer instead
// of a 64-bit integer. However, this is uncommon in practice.
//
// 9999999999999999999 < 2**64 so we can accommodate 19 digits.
// If we have a decimal separator, then digit_count - 1 is the number of digits, but we
// may not have a decimal separator!
if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) {
// Ok, chances are good that we had an overflow!
// this is almost never going to get called!!!
// we start anew, going slowly!!!
// This will happen in the following examples:
// 10000000000000000000000000000000000000000000e+308
// 3.1415926535897932384626433832795028841971693993751
//
// NOTE: We do not pass a reference to the to slow_float_parsing. If we passed our writer
// reference to it, it would force it to be stored in memory, preventing the compiler from
// picking it apart and putting into registers. i.e. if we pass it as reference,
// it gets slow.
double d;
error_code error = slow_float_parsing(src, &d);
writer.append_double(d);
return error;
}
// NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other
// way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331
// To future reader: we'd love if someone found a better way, or at least could explain this result!
if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) {
//
// Important: smallest_power is such that it leads to a zero value.
// Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero
// so something x 10^-343 goes to zero, but not so with something x 10^-342.
static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough");
//
if((exponent < simdjson::internal::smallest_power) || (i == 0)) {
// E.g. Parse "-0.0e-999" into the same value as "-0.0". See https://en.wikipedia.org/wiki/Signed_zero
WRITE_DOUBLE(negative ? -0.0 : 0.0, src, writer);
return SUCCESS;
} else { // (exponent > largest_power) and (i != 0)
// We have, for sure, an infinite value and simdjson refuses to parse infinite values.
return INVALID_NUMBER(src);
}
}
double d;
if (!compute_float_64(exponent, i, negative, d)) {
// we are almost never going to get here.
if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); }
}
WRITE_DOUBLE(d, src, writer);
return SUCCESS;
}
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer);
// for performance analysis, it is sometimes useful to skip parsing
#ifdef SIMDJSON_SKIPNUMBERPARSING
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const, W &writer) {
writer.append_s64(0); // always write zero
return SUCCESS; // always succeeds
}
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * const) noexcept { return 0; }
simdjson_unused simdjson_inline bool is_negative(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t *) noexcept { return false; }
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t *) noexcept { return number_type::signed_integer; }
#else
// parse the number at src
// define JSON_TEST_NUMBERS for unit testing
//
// It is assumed that the number is followed by a structural ({,},],[) character
// or a white space character. If that is not the case (e.g., when the JSON
// document is made of a single number), then it is necessary to copy the
// content and append a space before calling this function.
//
// Our objective is accurate parsing (ULP of 0) at high speed.
template<typename W>
simdjson_warn_unused simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while (parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) {
#if SIMDJSON_ENABLE_NAN_INF
// By this point, we know that our input does not begin with a digit. We will attempt
// to handle NaN/Infinity.
double d;
if (compute_nan_inf(p, negative, d)) {
writer.append_double(d);
return SUCCESS;
}
#endif
return INVALID_NUMBER(src);
}
//
// Handle floats if there is a . or e (or both)
//
int64_t exponent = 0;
bool is_float = false;
if ('.' == *p) {
is_float = true;
++p;
SIMDJSON_TRY( parse_decimal_after_separator(src, p, i, exponent) );
digit_count = int(p - start_digits); // used later to guard against overflows
}
if (('e' == *p) || ('E' == *p)) {
is_float = true;
++p;
SIMDJSON_TRY( parse_exponent(src, p, exponent) );
}
if (is_float) {
const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p);
SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) );
if (dirty_end) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// The longest negative 64-bit number is 19 digits.
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
size_t longest_digit_count = negative ? 19 : 20;
if (digit_count > longest_digit_count) { return BIGINT_NUMBER(src); }
if (digit_count == longest_digit_count) {
if (negative) {
// Anything negative above INT64_MAX+1 is invalid
if (i > uint64_t(INT64_MAX)+1) { return BIGINT_NUMBER(src); }
WRITE_INTEGER(~i+1, src, writer);
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
} else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return BIGINT_NUMBER(src); }
}
// Write unsigned if it does not fit in a signed integer.
if (i > uint64_t(INT64_MAX)) {
WRITE_UNSIGNED(i, src, writer);
} else {
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(i == 0 && negative) {
// We have to write -0.0 instead of 0
WRITE_DOUBLE(-0.0, src, writer);
} else {
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
}
#else
WRITE_INTEGER(negative ? (~i+1) : i, src, writer);
#endif
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); }
return SUCCESS;
}
// Inlineable functions
namespace {
// This table can be used to characterize the final character of an integer
// string. For JSON structural character and allowable white space characters,
// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise
// we return NUMBER_ERROR.
// Optimization note: we could easily reduce the size of the table by half (to 128)
// at the cost of an extra branch.
// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits):
static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast");
static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast");
const uint8_t integer_string_finisher[256] = {
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR,
SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR,
NUMBER_ERROR};
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept {
const uint8_t *p = src;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from 0 to 18,446,744,073,709,551,615
simdjson_unused simdjson_inline simdjson_result<uint64_t> parse_unsigned_in_string(const uint8_t * const src) noexcept {
const uint8_t *p = src + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// The longest positive 64-bit number is 20 digits.
// We do it this way so we don't trigger this branch unless we must.
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > 20))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if (*p != '"') { return NUMBER_ERROR; }
if (digit_count == 20) {
// Positive overflow check:
// - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the
// biggest uint64_t.
// - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX.
// If we got here, it's a 20 digit number starting with the digit "1".
// - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller
// than 1,553,255,926,290,448,384.
// - That is smaller than the smallest possible 20-digit number the user could write:
// 10,000,000,000,000,000,000.
// - Therefore, if the number is positive and lower than that, it's overflow.
// - The value we are looking at is less than or equal to INT64_MAX.
//
// Note: we use src[1] and not src[0] because src[0] is the quote character in this
// instance.
if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; }
}
return i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
parse_integer_digits(p, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept {
//
// Check for minus sign
//
if(src == src_end) { return NUMBER_ERROR; }
bool negative = (*src == '-');
const uint8_t *p = src + uint8_t(negative);
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = p;
uint64_t i = 0;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(p - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*p)) {
// return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
simdjson_unused simdjson_inline simdjson_result<int64_t> parse_integer_in_string(const uint8_t *src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
// PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare
const uint8_t *const start_digits = src;
uint64_t i = 0;
parse_integer_digits(src, i);
// If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error.
// Optimization note: size_t is expected to be unsigned.
size_t digit_count = size_t(src - start_digits);
// We go from
// -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
// so we can never represent numbers that have more than 19 digits.
size_t longest_digit_count = 19;
// Optimization note: the compiler can probably merge
// ((digit_count == 0) || (digit_count > longest_digit_count))
// into a single branch since digit_count is unsigned.
if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; }
// Here digit_count > 0.
if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; }
// We can do the following...
// if (!jsoncharutils::is_structural_or_whitespace(*src)) {
// return (*src == '.' || *src == 'e' || *src == 'E') ? INCORRECT_TYPE : NUMBER_ERROR;
// }
// as a single table lookup:
if(*src != '"') { return NUMBER_ERROR; }
// Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX.
// Performance note: This check is only needed when digit_count == longest_digit_count but it is
// so cheap that we might as well always make it.
if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; }
return negative ? (~i+1) : i;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no loading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
double d;
if (compute_nan_inf(p, negative, d)) { return d; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number into a binary32 (float) value.
//
// This mirrors parse_double, but it rounds to binary32 directly instead of
// rounding to binary64 and then to binary32: the latter is subject to double
// rounding and would not always produce the nearest float.
simdjson_unused simdjson_inline simdjson_result<float> parse_float(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, the number may be nan or infinity.
// Attempt to compute those, and return on success.
float f;
if (compute_nan_inf(p, negative, f)) { return f; }
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept {
return (*src == '-');
}
simdjson_unused simdjson_inline simdjson_result<bool> is_integer(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; }
return false;
}
simdjson_unused simdjson_inline simdjson_result<number_type> get_number_type(const uint8_t * src) noexcept {
bool negative = (*src == '-');
src += uint8_t(negative);
const uint8_t *p = src;
while(static_cast<uint8_t>(*p - '0') <= 9) { p++; }
size_t digit_count = size_t(p - src);
if ( p == src ) { return NUMBER_ERROR; }
if (jsoncharutils::is_structural_or_whitespace(*p)) {
static const uint8_t * smaller_big_integer = reinterpret_cast<const uint8_t *>("9223372036854775808");
// We have an integer.
if(simdjson_unlikely(digit_count > 20)) {
return number_type::big_integer;
}
// If the number is negative and valid, it must be a signed integer.
if(negative) {
if (simdjson_unlikely(digit_count > 19)) return number_type::big_integer;
if (simdjson_unlikely(digit_count == 19 && memcmp(src, smaller_big_integer, 19) > 0)) {
return number_type::big_integer;
}
#if SIMDJSON_MINUS_ZERO_AS_FLOAT
if(digit_count == 1 && src[0] == '0') {
// We have to write -0.0 instead of 0
return number_type::floating_point_number;
}
#endif
return number_type::signed_integer;
}
// Let us check if we have a big integer (>=2**64).
static const uint8_t * two_to_sixtyfour = reinterpret_cast<const uint8_t *>("18446744073709551616");
if((digit_count > 20) || (digit_count == 20 && memcmp(src, two_to_sixtyfour, 20) >= 0)) {
return number_type::big_integer;
}
// The number is positive and smaller than 18446744073709551616 (or 2**64).
// We want values larger or equal to 9223372036854775808 to be unsigned
// integers, and the other values to be signed integers.
if((digit_count == 20) || (digit_count >= 19 && memcmp(src, smaller_big_integer, 19) >= 0)) {
return number_type::unsigned_integer;
}
return number_type::signed_integer;
}
// Hopefully, we have 'e' or 'E' or '.'.
return number_type::floating_point_number;
}
// Never read at src_end or beyond
simdjson_unused simdjson_inline simdjson_result<double> parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept {
if(src == src_end) { return NUMBER_ERROR; }
//
// Check for minus sign
//
bool negative = (*src == '-');
src += uint8_t(negative);
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
if(p == src_end) { return NUMBER_ERROR; }
p += parse_digit(*p, i);
bool leading_zero = (i == 0);
while ((p != src_end) && parse_digit(*p, i)) { p++; }
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) { return INCORRECT_TYPE; }
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely((p != src_end) && (*p == '.'))) {
p++;
const uint8_t *start_decimal_digits = p;
if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits
p++;
while ((p != src_end) && parse_digit(*p, i)) { p++; }
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = start_digits-src > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if ((p != src_end) && (*p == 'e' || *p == 'E')) {
p++;
if(p == src_end) { return NUMBER_ERROR; }
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while ((p != src_end) && parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), src_end, &d)) {
return NUMBER_ERROR;
}
return d;
}
simdjson_unused simdjson_inline simdjson_result<double> parse_double_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
double inf = std::numeric_limits<double>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<double>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
double d;
if (simdjson_likely(!overflow)) {
if (compute_float_64(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
// Parse a JSON number held inside a JSON string into a binary32 (float) value.
// See parse_float for why we do not simply round parse_double_in_string.
simdjson_unused simdjson_inline simdjson_result<float> parse_float_in_string(const uint8_t * src) noexcept {
//
// Check for minus sign
//
bool negative = (*(src + 1) == '-');
src += uint8_t(negative) + 1;
//
// Parse the integer part.
//
uint64_t i = 0;
const uint8_t *p = src;
bool leading_zero;
parse_float_integer_digits(p, i, leading_zero);
// no integer digits, or 0123 (zero must be solo)
if ( p == src ) {
#if SIMDJSON_ENABLE_NAN_INF
// If there are no leading digits, attempt to parse numbers that are either
// NaN or Infinity
if (atomparsing::is_valid_inf_in_string(src)) {
float inf = std::numeric_limits<float>::infinity();
return negative ? -inf : inf;
}
if (atomparsing::is_valid_nan_in_string(src)) {
return std::numeric_limits<float>::quiet_NaN();
}
#endif
return INCORRECT_TYPE;
}
if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; }
//
// Parse the decimal part.
//
int64_t exponent = 0;
bool overflow;
if (simdjson_likely(*p == '.')) {
p++;
const uint8_t *start_decimal_digits = p;
parse_fraction_digits(p, i);
if (p == start_decimal_digits) { return NUMBER_ERROR; } // no decimal digits
exponent = -(p - start_decimal_digits);
// Overflow check. More than 19 digits (minus the decimal) may be overflow.
overflow = p-src-1 > 19;
if (simdjson_unlikely(overflow && leading_zero)) {
// Skip leading 0.00000 and see if it still overflows
const uint8_t *start_digits = src + 2;
while (*start_digits == '0') { start_digits++; }
overflow = p-start_digits > 19;
}
} else {
overflow = p-src > 19;
}
//
// Parse the exponent
//
if (*p == 'e' || *p == 'E') {
p++;
bool exp_neg = *p == '-';
p += exp_neg || *p == '+';
uint64_t exp = 0;
const uint8_t *start_exp_digits = p;
while (parse_digit(*p, exp)) { p++; }
// no exp digits, or 20+ exp digits
if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; }
exponent += exp_neg ? 0-exp : exp;
}
if (*p != '"') { return NUMBER_ERROR; }
overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power;
//
// Assemble (or slow-parse) the float
//
float d;
if (simdjson_likely(!overflow)) {
if (compute_float_32(exponent, i, negative, d)) { return d; }
}
if (!parse_float_fallback(src - uint8_t(negative), &d)) {
return NUMBER_ERROR;
}
return d;
}
} // unnamed namespace
#endif // SIMDJSON_SKIPNUMBERPARSING
} // namespace numberparsing
inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept {
switch (type) {
case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break;
case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break;
case number_type::floating_point_number: out << "floating-point number (binary64)"; break;
case number_type::big_integer: out << "big integer"; break;
default: SIMDJSON_UNREACHABLE();
}
return out;
}
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_NUMBERPARSING_H
/* end file simdjson/generic/numberparsing.h for rvv_vls */
/* including simdjson/generic/implementation_simdjson_result_base-inl.h for rvv_vls: #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* begin file simdjson/generic/implementation_simdjson_result_base-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
//
// internal::implementation_simdjson_result_base<T> inline implementation
//
template<typename T>
simdjson_inline void implementation_simdjson_result_base<T>::tie(T &value, error_code &error) && noexcept {
error = this->second;
if (!error) {
value = std::forward<implementation_simdjson_result_base<T>>(*this).first;
}
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::get(T &value) && noexcept {
error_code error;
std::forward<implementation_simdjson_result_base<T>>(*this).tie(value, error);
return error;
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base<T>::error() const noexcept {
return this->second;
}
template<typename T>
simdjson_warn_unused simdjson_inline bool implementation_simdjson_result_base<T>::has_value() const noexcept {
return this->error() == SUCCESS;
}
#if SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::operator*() & noexcept(false) {
return this->value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::operator*() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).value();
}
template<typename T>
simdjson_inline T* implementation_simdjson_result_base<T>::operator->() noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline const T* implementation_simdjson_result_base<T>::operator->() const noexcept(false) {
if (this->error()) { throw simdjson_error(this->error()); }
return &this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::take_value() && noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::operator T&&() && noexcept(false) {
return std::forward<implementation_simdjson_result_base<T>>(*this).take_value();
}
#endif // SIMDJSON_EXCEPTIONS
template<typename T>
simdjson_inline const T& implementation_simdjson_result_base<T>::value_unsafe() const& noexcept {
return this->first;
}
template<typename T>
simdjson_inline T& implementation_simdjson_result_base<T>::value_unsafe() & noexcept {
return this->first;
}
template<typename T>
simdjson_inline T&& implementation_simdjson_result_base<T>::value_unsafe() && noexcept {
return std::forward<T>(this->first);
}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value, error_code error) noexcept
: first{std::forward<T>(value)}, second{error} {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(error_code error) noexcept
: implementation_simdjson_result_base(T{}, error) {}
template<typename T>
simdjson_inline implementation_simdjson_result_base<T>::implementation_simdjson_result_base(T &&value) noexcept
: implementation_simdjson_result_base(std::forward<T>(value), SUCCESS) {}
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_IMPLEMENTATION_SIMDJSON_RESULT_BASE_INL_H
/* end file simdjson/generic/implementation_simdjson_result_base-inl.h for rvv_vls */
/* end file simdjson/generic/amalgamated.h for rvv_vls */
/* including simdjson/rvv-vls/end.h: #include "simdjson/rvv-vls/end.h" */
/* begin file simdjson/rvv-vls/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/* undefining SIMDJSON_IMPLEMENTATION from "rvv_vls" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/rvv-vls/end.h */
#endif // SIMDJSON_RVV_VLS_H
/* end file simdjson/rvv-vls.h */
#else
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
#endif
/* undefining SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_CONDITIONAL_INCLUDE
#endif // SIMDJSON_BUILTIN_H
/* end file simdjson/builtin.h */
/* skipped duplicate #include "simdjson/builtin/base.h" */
/* including simdjson/generic/builder/dependencies.h: #include "simdjson/generic/builder/dependencies.h" */
/* begin file simdjson/generic/builder/dependencies.h */
#ifdef SIMDJSON_CONDITIONAL_INCLUDE
#error simdjson/generic/builder/dependencies.h must be included before defining SIMDJSON_CONDITIONAL_INCLUDE!
#endif
#ifndef SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H
#define SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H
// Internal headers needed for builder generics.
// All includes not under simdjson/generic/builder must be here!
// Otherwise, amalgamation will fail.
/* skipped duplicate #include "simdjson/concepts.h" */
/* skipped duplicate #include "simdjson/dom/fractured_json.h" */
/* including simdjson/annotations.h: #include "simdjson/annotations.h" */
/* begin file simdjson/annotations.h */
#ifndef SIMDJSON_ANNOTATIONS_H
#define SIMDJSON_ANNOTATIONS_H
/**
* @file annotations.h
* @brief Provides compile-time annotations for simdjson structures.
* This header defines annotations that can be applied to data members of structures
* (and to the structures and enumerations themselves) to control how they are
* serialized/deserialized with simdjson. The set of annotations is modelled after
* the attributes of the Rust serde library.
*
* Member annotations:
*
* [[= simdjson::rename<"name">]] use "name" as the JSON key
* [[= simdjson::alias<"a", "b">]] also accept "a" and "b" when deserializing
* [[= simdjson::skip]] never serialize nor deserialize
* [[= simdjson::skip_serializing]] never serialize
* [[= simdjson::skip_deserializing]] never deserialize (keeps its current value)
* [[= simdjson::skip_serializing_if<pred>]] do not serialize when pred(value) is true
* [[= simdjson::default_value]] a missing key is not an error
* [[= simdjson::default_from<factory>]] a missing key sets the member to factory()
* [[= simdjson::with<Adapter>]] custom (de)serialization via Adapter
* [[= simdjson::flatten]] inline the members of a nested structure
*
* Structure (container) annotations:
*
* [[= simdjson::rename_all<simdjson::case_style::camel_case>]] rename every member
* [[= simdjson::default_value]] no missing key is an error
* [[= simdjson::deny_unknown_fields]] unknown keys are a deserialization error
* [[= simdjson::transparent]] (de)serialize as the single member
*
* Enumeration annotations: rename_all on the enumeration, rename and alias on the
* enumerators (e.g., `enum class color { red [[= simdjson::rename<"RED">]] };`).
*
* This is currently experimental and subject to change (syntax and semantics may evolve).
*/
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <string>
#include <string_view>
#include <vector>
namespace simdjson {
// Structural compile-time string -- char array avoids the pointer-based
// 'reflect_constant failed' that occurs with const char* / string_view members.
template <size_t N>
struct fixed_string {
char data[N];
consteval fixed_string(const char (&s)[N]) noexcept {
for (size_t i = 0; i < N; ++i) { data[i] = s[i]; }
}
consteval std::string_view view() const noexcept { return {data, N - 1}; }
consteval bool operator==(const fixed_string&) const noexcept = default;
};
/**
* Naming conventions for simdjson::rename_all, mirroring serde's rename_all.
* Except for lowercase and uppercase, the C++ identifier is first split into
* words at underscores and at case changes ("userId", "user_id" and "UserId" all
* give the words "user" and "id"; "HTTPServer" gives "HTTP" and "Server"), and
* the words are then joined according to the convention.
*/
enum class case_style {
lowercase, ///< every letter lowercased, nothing else changes: userId -> userid
uppercase, ///< every letter uppercased, nothing else changes: user_id -> USER_ID
pascal_case, ///< UserId
camel_case, ///< userId
snake_case, ///< user_id
screaming_snake_case, ///< USER_ID
kebab_case, ///< user-id
screaming_kebab_case ///< USER-ID
};
namespace detail {
template <fixed_string Name>
struct rename_t {
static constexpr auto name = Name;
// Exposed as a pointer and a size: std::meta::extract requires structural types.
static constexpr const char *key_data = Name.data;
static constexpr size_t key_size = Name.view().size();
};
template <fixed_string... Names>
struct alias_t {
static_assert(sizeof...(Names) > 0, "simdjson::alias requires at least one name");
static constexpr std::string_view keys[] = {Names.view()...};
static constexpr const std::string_view *keys_data = keys;
static constexpr size_t keys_count = sizeof...(Names);
};
struct skip_tag {};
struct skip_serializing_tag {};
struct skip_deserializing_tag {};
template <auto Predicate>
struct skip_serializing_if_t {
static constexpr auto predicate = Predicate;
};
struct default_value_tag {};
template <auto Factory>
struct default_from_t {
static constexpr auto factory = Factory;
};
template <typename Adapter>
struct with_t {
using adapter = Adapter;
};
template <case_style Style>
struct rename_all_t {
static constexpr case_style style = Style;
};
struct deny_unknown_fields_tag {};
struct transparent_tag {};
struct flatten_tag {};
// Predicates usable with skip_serializing_if.
struct is_none_t {
template <typename T>
constexpr bool operator()(const T& v) const noexcept { return !v; }
};
struct is_empty_t {
template <typename T>
constexpr bool operator()(const T& v) const noexcept { return v.empty(); }
};
} // namespace detail
// Usage: [[= simdjson::rename<"first_name">]] std::string firstName;
template <fixed_string Name>
inline constexpr detail::rename_t<Name> rename{};
// Usage: [[= simdjson::alias<"userName", "login">]] std::string user_name;
// The aliases are accepted (in addition to the regular key) when deserializing.
// Serialization always uses the regular key. If the JSON object contains more
// than one of the names, which one is used is unspecified.
template <fixed_string... Names>
inline constexpr detail::alias_t<Names...> alias{};
// Usage: [[= simdjson::skip]] int internalCache;
inline constexpr detail::skip_tag skip{};
// Usage: [[= simdjson::skip_serializing]] std::string password;
inline constexpr detail::skip_serializing_tag skip_serializing{};
// Usage: [[= simdjson::skip_deserializing]] int computed;
// The member is never assigned during deserialization (it keeps its current
// value) and a matching key in the JSON input is treated as unknown.
inline constexpr detail::skip_deserializing_tag skip_deserializing{};
// Usage: [[= simdjson::skip_serializing_if<simdjson::is_none>]] std::optional<int> x;
// The predicate is called with the member value; when it returns true, the key
// is omitted from the output.
template <auto Predicate>
inline constexpr detail::skip_serializing_if_t<Predicate> skip_serializing_if{};
// Predicate: true for an empty std::optional, a null smart pointer, etc.
inline constexpr detail::is_none_t is_none{};
// Predicate: true for an empty string or container.
inline constexpr detail::is_empty_t is_empty{};
// Usage: [[= simdjson::default_value]] int port = 8080;
// When the key is missing from the JSON input, the member is left untouched
// (with get<T>(), it keeps its default member initializer) instead of reporting
// NO_SUCH_FIELD. Applied to a structure, it applies to all of its members.
inline constexpr detail::default_value_tag default_value{};
// Usage: [[= simdjson::default_from<make_port>]] int port;
// When the key is missing from the JSON input, the member is assigned the
// result of calling the factory (a constexpr callable taking no argument, such
// as a captureless lambda or a pointer to a function).
template <auto Factory>
inline constexpr detail::default_from_t<Factory> default_from{};
// Usage: [[= simdjson::with<unix_time>]] std::chrono::system_clock::time_point t;
// Adapter is a type that provides one or both of
// static void serialize(simdjson::builder::string_builder &b, const T &value);
// static simdjson::error_code deserialize(simdjson::ondemand::value &v, T &out);
// (the parameters may also be declared auto&). When one of them is missing, the
// default behaviour is used in that direction.
template <typename Adapter>
inline constexpr detail::with_t<Adapter> with{};
// Usage: [[= simdjson::flatten]] pagination page;
// The members of the nested structure are (de)serialized as if they were members
// of the enclosing structure: {"id":1,"limit":10,"offset":0} rather than
// {"id":1,"page":{"limit":10,"offset":0}}. The nested structure's own annotations
// (rename_all, default_value, ...) apply to its members.
inline constexpr detail::flatten_tag flatten{};
// Usage: struct [[= simdjson::rename_all<simdjson::case_style::camel_case>]] S {...};
// Also applies to enumerations. An explicit rename on a member takes precedence.
template <case_style Style>
inline constexpr detail::rename_all_t<Style> rename_all{};
// Usage: struct [[= simdjson::deny_unknown_fields]] S {...};
// Deserialization fails with UNKNOWN_FIELD when the JSON object has a key that
// is not deserialized into a member (including the keys of skipped members).
inline constexpr detail::deny_unknown_fields_tag deny_unknown_fields{};
// Usage: struct [[= simdjson::transparent]] user_id { int64_t value; };
// A structure with a single data member is (de)serialized as that member alone:
// user_id{42} becomes 42 rather than {"value":42}.
inline constexpr detail::transparent_tag transparent{};
namespace detail {
// True when the entity (a data member, an enumerator or a type) carries an
// annotation of type tag.
consteval bool has_annotation(std::meta::info entity, std::meta::info tag) {
return !std::meta::annotations_of_with_type(entity, tag).empty();
}
// Returns the type of the (first) annotation of entity that is a specialization
// of the class template tmpl, or std::meta::info{} when there is none.
consteval std::meta::info annotation_of_template(std::meta::info entity, std::meta::info tmpl) {
for (std::meta::info ann : std::meta::annotations_of(entity)) {
std::meta::info type = std::meta::type_of(ann);
if (std::meta::has_template_arguments(type) && std::meta::template_of(type) == tmpl) {
return type;
}
}
return std::meta::info{};
}
// Value of the static data member `name` of the class `type`.
template <typename T>
consteval T static_member_value(std::meta::info type, std::string_view name) {
for (std::meta::info m : std::meta::static_data_members_of(type, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == name) {
return std::meta::extract<T>(m);
}
}
return T{};
}
consteval bool is_upper(char c) { return c >= 'A' && c <= 'Z'; }
consteval bool is_lower(char c) { return c >= 'a' && c <= 'z'; }
consteval bool is_digit(char c) { return c >= '0' && c <= '9'; }
consteval char to_upper(char c) { return is_lower(c) ? char(c - 'a' + 'A') : c; }
consteval char to_lower(char c) { return is_upper(c) ? char(c - 'A' + 'a') : c; }
// Split an identifier into words: at underscores, at a lowercase letter or digit
// followed by an uppercase letter (userId), and before the last capital of an
// acronym followed by a lowercase letter (HTTPServer -> HTTP, Server).
consteval std::vector<std::string> split_identifier(std::string_view id) {
std::vector<std::string> words;
std::string current;
for (size_t i = 0; i < id.size(); i++) {
char c = id[i];
if (c == '_') {
if (!current.empty()) { words.push_back(current); current.clear(); }
continue;
}
if (is_upper(c) && !current.empty()) {
char prev = current.back();
bool next_is_lower = (i + 1 < id.size()) && is_lower(id[i + 1]);
if (is_lower(prev) || is_digit(prev) || (is_upper(prev) && next_is_lower)) {
words.push_back(current);
current.clear();
}
}
current.push_back(c);
}
if (!current.empty()) { words.push_back(current); }
return words;
}
consteval std::string apply_case_style(std::string_view id, case_style style) {
std::string result;
if (style == case_style::lowercase || style == case_style::uppercase) {
for (char c : id) {
result.push_back(style == case_style::lowercase ? to_lower(c) : to_upper(c));
}
return result;
}
std::vector<std::string> words = split_identifier(id);
for (size_t w = 0; w < words.size(); w++) {
const std::string &word = words[w];
if (style == case_style::pascal_case || style == case_style::camel_case) {
for (size_t i = 0; i < word.size(); i++) {
bool capital = (i == 0) && (style == case_style::pascal_case || w > 0);
result.push_back(capital ? to_upper(word[i]) : to_lower(word[i]));
}
} else {
bool upper = style == case_style::screaming_snake_case || style == case_style::screaming_kebab_case;
bool kebab = style == case_style::kebab_case || style == case_style::screaming_kebab_case;
if (w > 0) { result.push_back(kebab ? '-' : '_'); }
for (char c : word) { result.push_back(upper ? to_upper(c) : to_lower(c)); }
}
}
return result;
}
// The JSON key for a data member or an enumerator: an explicit rename wins,
// then the rename_all of the enclosing structure or enumeration, then the C++
// identifier.
consteval std::string_view json_key_name(std::meta::info entity) {
std::meta::info rename_type = annotation_of_template(entity, ^^rename_t);
if (rename_type != std::meta::info{}) {
return std::define_static_string(std::string_view{
static_member_value<const char *>(rename_type, "key_data"),
static_member_value<size_t>(rename_type, "key_size")});
}
std::meta::info rename_all_type = annotation_of_template(std::meta::parent_of(entity), ^^rename_all_t);
if (rename_all_type != std::meta::info{}) {
case_style style = static_member_value<case_style>(rename_all_type, "style");
return std::define_static_string(apply_case_style(std::meta::identifier_of(entity), style));
}
return std::define_static_string(std::meta::identifier_of(entity));
}
// The keys accepted when deserializing a data member or an enumerator: the JSON
// key first, followed by the aliases (if any), in declaration order.
consteval std::vector<std::string_view> json_key_names(std::meta::info entity) {
std::vector<std::string_view> names{json_key_name(entity)};
for (std::meta::info ann : std::meta::annotations_of(entity)) {
std::meta::info type = std::meta::type_of(ann);
if (std::meta::has_template_arguments(type) && std::meta::template_of(type) == ^^alias_t) {
const std::string_view *keys = static_member_value<const std::string_view *>(type, "keys_data");
size_t count = static_member_value<size_t>(type, "keys_count");
for (size_t i = 0; i < count; i++) { names.push_back(keys[i]); }
}
}
return names;
}
// The structure type of a member annotated with flatten.
consteval std::meta::info flattened_type(std::meta::info mem) {
if (std::meta::is_reference_type(std::meta::type_of(mem))) {
// A reference member could refer back to the enclosing structure: the
// flattening would never terminate.
throw std::meta::exception(u8"simdjson::flatten requires a member that is not a reference", mem);
}
std::meta::info type = std::meta::remove_cvref(std::meta::type_of(mem));
if (!std::meta::is_class_type(type)) {
throw std::meta::exception(u8"simdjson::flatten requires a member of class type", mem);
}
return type;
}
// The single data member of a structure annotated with transparent: the only
// member that is not annotated with skip.
consteval std::meta::info transparent_member(std::meta::info type) {
std::vector<std::meta::info> members;
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!has_annotation(mem, ^^skip_tag)) { members.push_back(mem); }
}
if (members.size() != 1) {
throw std::meta::exception(u8"simdjson::transparent requires exactly one data member (not counting skipped members)", type);
}
return members[0];
}
} // namespace detail
// Returns the JSON key for a reflected data member (or enumerator).
template <auto dm>
consteval const char* get_json_key_name() {
return detail::json_key_name(dm).data();
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_ANNOTATIONS_H
/* end file simdjson/annotations.h */
#endif // SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H
/* end file simdjson/generic/builder/dependencies.h */
/* defining SIMDJSON_CONDITIONAL_INCLUDE */
#define SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64)
/* including simdjson/arm64/builder.h: #include "simdjson/arm64/builder.h" */
/* begin file simdjson/arm64/builder.h */
#ifndef SIMDJSON_ARM64_BUILDER_H
#define SIMDJSON_ARM64_BUILDER_H
/* including simdjson/arm64/begin.h: #include "simdjson/arm64/begin.h" */
/* begin file simdjson/arm64/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "arm64" */
#define SIMDJSON_IMPLEMENTATION arm64
/* including simdjson/arm64/base.h: #include "simdjson/arm64/base.h" */
/* begin file simdjson/arm64/base.h */
#ifndef SIMDJSON_ARM64_BASE_H
#define SIMDJSON_ARM64_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for NEON (ARMv8).
*/
namespace arm64 {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_BASE_H
/* end file simdjson/arm64/base.h */
/* including simdjson/arm64/intrinsics.h: #include "simdjson/arm64/intrinsics.h" */
/* begin file simdjson/arm64/intrinsics.h */
#ifndef SIMDJSON_ARM64_INTRINSICS_H
#define SIMDJSON_ARM64_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// This should be the correct header whether
// you use visual studio or other compilers.
#include <arm_neon.h>
static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient padding for arm64");
#endif // SIMDJSON_ARM64_INTRINSICS_H
/* end file simdjson/arm64/intrinsics.h */
/* including simdjson/arm64/bitmanipulation.h: #include "simdjson/arm64/bitmanipulation.h" */
/* begin file simdjson/arm64/bitmanipulation.h */
#ifndef SIMDJSON_ARM64_BITMANIPULATION_H
#define SIMDJSON_ARM64_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
// We sometimes call leading_zeroes on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true.
// (See below.)
SIMDJSON_NO_SANITIZE_UNDEFINED
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return vaddv_u8(vcnt_u8(vcreate_u8(input_num)));
#else
// if the system supports SVE or CSSC, __builtin_popcountll
// might be compiled to fewer single instructions. For CSSC,
// __builtin_popcountll is compiled to a single instruction.
return __builtin_popcountll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if defined(__GNUC__) // catches clang and gcc
/**
* ARM has a fast 64-bit "bit reversal function" that is handy. However,
* it is not generally available as an intrinsic function under Visual
* Studio (though this might be changing). Even under clang/gcc, we
* apparently need to invoke inline assembly.
*/
/*
* We use SIMDJSON_PREFER_REVERSE_BITS as a hint that algorithms that
* work well with bit reversal may use it.
*/
#define SIMDJSON_PREFER_REVERSE_BITS 1
/* reverse the bits */
simdjson_inline uint64_t reverse_bits(uint64_t input_num) {
uint64_t rev_bits;
__asm("rbit %0, %1" : "=r"(rev_bits) : "r"(input_num));
return rev_bits;
}
/**
* Flips bit at index 63 - lz. Thus if you have 'leading_zeroes' leading zeroes,
* then this will set to zero the leading bit. It is possible for leading_zeroes to be
* greating or equal to 63 in which case we trigger undefined behavior, but the output
* of such undefined behavior is never used.
**/
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) {
return rev_bits ^ (uint64_t(0x8000000000000000) >> leading_zeroes);
}
#endif
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_BITMANIPULATION_H
/* end file simdjson/arm64/bitmanipulation.h */
/* including simdjson/arm64/bitmask.h: #include "simdjson/arm64/bitmask.h" */
/* begin file simdjson/arm64/bitmask.h */
#ifndef SIMDJSON_ARM64_BITMASK_H
#define SIMDJSON_ARM64_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
/////////////
// We could do this with PMULL, but it is apparently slow.
//
//#ifdef __ARM_FEATURE_CRYPTO // some ARM processors lack this extension
//return vmull_p64(-1ULL, bitmask);
//#else
// Analysis by @sebpop:
// When diffing the assembly for src/stage1_find_marks.cpp I see that the eors are all spread out
// in between other vector code, so effectively the extra cycles of the sequence do not matter
// because the GPR units are idle otherwise and the critical path is on the FP side.
// Also the PMULL requires two extra fmovs: GPR->FP (3 cycles in N1, 5 cycles in A72 )
// and FP->GPR (2 cycles on N1 and 5 cycles on A72.)
///////////
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif
/* end file simdjson/arm64/bitmask.h */
/* including simdjson/arm64/numberparsing_defs.h: #include "simdjson/arm64/numberparsing_defs.h" */
/* begin file simdjson/arm64/numberparsing_defs.h */
#ifndef SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
#define SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#if SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
// __umulh requires intrin.h
#include <intrin.h>
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
namespace simdjson {
namespace arm64 {
namespace numberparsing {
// we don't have SSE, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace arm64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
/* end file simdjson/arm64/numberparsing_defs.h */
/* including simdjson/arm64/simd.h: #include "simdjson/arm64/simd.h" */
/* begin file simdjson/arm64/simd.h */
#ifndef SIMDJSON_ARM64_SIMD_H
#define SIMDJSON_ARM64_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
namespace simd {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
namespace {
// Start of private section with Visual Studio workaround
#ifndef simdjson_make_uint8x16_t
#define simdjson_make_uint8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
x13, x14, x15, x16) \
([=]() { \
uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
x9, x10, x11, x12, x13, x14, x15, x16}; \
return vld1q_u8(array); \
}())
#endif
#ifndef simdjson_make_int8x16_t
#define simdjson_make_int8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
x13, x14, x15, x16) \
([=]() { \
int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
x9, x10, x11, x12, x13, x14, x15, x16}; \
return vld1q_s8(array); \
}())
#endif
#ifndef simdjson_make_uint8x8_t
#define simdjson_make_uint8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
uint8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1_u8(array); \
}())
#endif
#ifndef simdjson_make_int8x8_t
#define simdjson_make_int8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
int8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1_s8(array); \
}())
#endif
#ifndef simdjson_make_uint16x8_t
#define simdjson_make_uint16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
uint16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1q_u16(array); \
}())
#endif
#ifndef simdjson_make_int16x8_t
#define simdjson_make_int16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
int16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1q_s16(array); \
}())
#endif
// End of private section with Visual Studio workaround
} // namespace
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
template<typename T>
struct simd8;
//
// Base class of simd8<uint8_t> and simd8<bool>, both of which use uint8x16_t internally.
//
template<typename T, typename Mask=simd8<bool>>
struct base_u8 {
uint8x16_t value;
static const int SIZE = sizeof(value);
// Conversion from/to SIMD register
simdjson_inline base_u8(const uint8x16_t _value) : value(_value) {}
simdjson_inline operator const uint8x16_t&() const { return this->value; }
simdjson_inline operator uint8x16_t&() { return this->value; }
// Bit operations
simdjson_inline simd8<T> operator|(const simd8<T> other) const { return vorrq_u8(*this, other); }
simdjson_inline simd8<T> operator&(const simd8<T> other) const { return vandq_u8(*this, other); }
simdjson_inline simd8<T> operator^(const simd8<T> other) const { return veorq_u8(*this, other); }
simdjson_inline simd8<T> bit_andnot(const simd8<T> other) const { return vbicq_u8(*this, other); }
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
simdjson_inline simd8<T>& operator|=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline simd8<T>& operator&=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline simd8<T>& operator^=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return vceqq_u8(lhs, rhs); }
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return vextq_u8(prev_chunk, *this, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base_u8<bool> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
static simdjson_inline simd8<bool> splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); }
simdjson_inline simd8(const uint8x16_t _value) : base_u8<bool>(_value) {}
// False constructor
simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {}
// Splat constructor
simdjson_inline simd8(bool _value) : simd8(splat(_value)) {}
// We return uint32_t instead of uint16_t because that seems to be more efficient for most
// purposes (cutting it down to uint16_t costs performance in some compilers).
simdjson_inline uint32_t to_bitmask() const {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
const uint8x16_t bit_mask = simdjson_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
#else
const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
#endif
auto minput = *this & bit_mask;
uint8x16_t tmp = vpaddq_u8(minput, minput);
tmp = vpaddq_u8(tmp, tmp);
tmp = vpaddq_u8(tmp, tmp);
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
// Integer umaxv, not a float compare: flush-to-zero would treat a denormal as zero.
simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base_u8<uint8_t> {
static simdjson_inline uint8x16_t splat(uint8_t _value) { return vmovq_n_u8(_value); }
static simdjson_inline uint8x16_t zero() { return vdupq_n_u8(0); }
static simdjson_inline uint8x16_t load(const uint8_t* values) { return vld1q_u8(values); }
simdjson_inline simd8(const uint8x16_t _value) : base_u8<uint8_t>(_value) {}
// Zero constructor
simdjson_inline simd8() : simd8(zero()) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[16]) : simd8(load(values)) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Member-by-member initialization
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(simdjson_make_uint8x16_t(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
#else
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(uint8x16_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
#endif
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Store to array
simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); }
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return vqaddq_u8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return vqsubq_u8(*this, other); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<uint8_t> operator+(const simd8<uint8_t> other) const { return vaddq_u8(*this, other); }
simdjson_inline simd8<uint8_t> operator-(const simd8<uint8_t> other) const { return vsubq_u8(*this, other); }
simdjson_inline simd8<uint8_t>& operator+=(const simd8<uint8_t> other) { *this = *this + other; return *this; }
simdjson_inline simd8<uint8_t>& operator-=(const simd8<uint8_t> other) { *this = *this - other; return *this; }
// Order-specific operations
simdjson_inline uint8_t max_val() const { return vmaxvq_u8(*this); }
simdjson_inline uint8_t min_val() const { return vminvq_u8(*this); }
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return vmaxq_u8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return vminq_u8(*this, other); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return vcleq_u8(*this, other); }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return vcgeq_u8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return vcltq_u8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return vcgtq_u8(*this, other); }
// Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's.
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this > other); }
// Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's.
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this < other); }
// Bit-specific operations
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return vtstq_u8(*this, bits); }
simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return vshrq_n_u8(*this, N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return vshlq_n_u8(*this, N); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint16_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]};
uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64);
// we increment by 0x08 the second half of the mask
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint8x16_t inc = simdjson_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
#else
uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
#endif
shufmask = vaddq_u8(shufmask, inc);
// this is the version "nearly pruned"
uint8x16_t pruned = vqtbl1q_u8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
uint8x16_t compactmask = vld1q_u8(reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
uint8x16_t answer = vqtbl1q_u8(pruned, compactmask);
vst1q_u8(reinterpret_cast<uint8_t*>(output), answer);
}
// Copies all bytes corresponding to a 0 in the low half of the mask (interpreted as a
// bitset) to output1, then those corresponding to a 0 in the high half to output2.
template<typename L>
simdjson_inline void compress_halves(uint16_t mask, L *output1, L *output2) const {
using internal::thintable_epi8;
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
uint8x8_t compactmask1 = vcreate_u8(thintable_epi8[mask1]);
uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]);
// we increment by 0x08 the second half of the mask
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
#else
uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
#endif
compactmask2 = vadd_u8(compactmask2, inc);
// store each result (with the second store possibly overlapping the first)
vst1_u8((uint8_t*)output1, vqtbl1_u8(*this, compactmask1));
vst1_u8((uint8_t*)output2, vqtbl1_u8(*this, compactmask2));
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
template<typename T>
simdjson_inline simd8<uint8_t> apply_lookup_16_to(const simd8<T> original) {
return vqtbl1q_u8(*this, simd8<uint8_t>(original));
}
};
// Signed bytes
template<>
struct simd8<int8_t> {
int8x16_t value;
static simdjson_inline simd8<int8_t> splat(int8_t _value) { return vmovq_n_s8(_value); }
static simdjson_inline simd8<int8_t> zero() { return vdupq_n_s8(0); }
static simdjson_inline simd8<int8_t> load(const int8_t values[16]) { return vld1q_s8(values); }
// Conversion from/to SIMD register
simdjson_inline simd8(const int8x16_t _value) : value{_value} {}
simdjson_inline operator const int8x16_t&() const { return this->value; }
simdjson_inline operator int8x16_t&() { return this->value; }
// Zero constructor
simdjson_inline simd8() : simd8(zero()) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(simdjson_make_int8x16_t(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
#else
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(int8x16_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
#endif
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Store to array
simdjson_inline void store(int8_t dst[16]) const { return vst1q_s8(dst, *this); }
// Explicit conversion to/from unsigned
//
// Under Visual Studio/ARM64 uint8x16_t and int8x16_t are apparently the same type.
// In theory, we could check this occurrence with std::same_as and std::enabled_if but it is C++14
// and relatively ugly and hard to read.
#ifndef SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {}
#endif
simdjson_inline explicit operator simd8<uint8_t>() const { return vreinterpretq_u8_s8(this->value); }
// Math
simdjson_inline simd8<int8_t> operator+(const simd8<int8_t> other) const { return vaddq_s8(*this, other); }
simdjson_inline simd8<int8_t> operator-(const simd8<int8_t> other) const { return vsubq_s8(*this, other); }
simdjson_inline simd8<int8_t>& operator+=(const simd8<int8_t> other) { *this = *this + other; return *this; }
simdjson_inline simd8<int8_t>& operator-=(const simd8<int8_t> other) { *this = *this - other; return *this; }
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return vmaxq_s8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return vminq_s8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return vcgtq_s8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return vcltq_s8(*this, other); }
simdjson_inline simd8<bool> operator==(const simd8<int8_t> other) const { return vceqq_s8(*this, other); }
template<int N=1>
simdjson_inline simd8<int8_t> prev(const simd8<int8_t> prev_chunk) const {
return vextq_s8(prev_chunk, *this, 16 - N);
}
// Perform a lookup assuming no value is larger than 16
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
template<typename T>
simdjson_inline simd8<int8_t> apply_lookup_16_to(const simd8<T> original) {
return vqtbl1q_s8(*this, simd8<uint8_t>(original));
}
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint64_t popcounts = vget_lane_u64(vreinterpret_u64_u8(vcnt_u8(vcreate_u8(~mask))), 0);
// compute the prefix sum of the popcounts of each byte
uint64_t offsets = popcounts * 0x0101010101010101;
this->chunks[0].compress_halves(uint16_t(mask), output, &output[popcounts & 0xFF]);
this->chunks[1].compress_halves(uint16_t(mask >> 16), &output[(offsets >> 8) & 0xFF], &output[(offsets >> 16) & 0xFF]);
this->chunks[2].compress_halves(uint16_t(mask >> 32), &output[(offsets >> 24) & 0xFF], &output[(offsets >> 32) & 0xFF]);
this->chunks[3].compress_halves(uint16_t(mask >> 48), &output[(offsets >> 40) & 0xFF], &output[(offsets >> 48) & 0xFF]);
return offsets >> 56;
}
simdjson_inline uint64_t to_bitmask() const {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
const uint8x16_t bit_mask = simdjson_make_uint8x16_t(
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
);
#else
const uint8x16_t bit_mask = {
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
};
#endif
// Add each of the elements next to each other, successively, to stuff each 8 byte mask into one.
uint8x16_t sum0 = vpaddq_u8(this->chunks[0] & bit_mask, this->chunks[1] & bit_mask);
uint8x16_t sum1 = vpaddq_u8(this->chunks[2] & bit_mask, this->chunks[3] & bit_mask);
sum0 = vpaddq_u8(sum0, sum1);
sum0 = vpaddq_u8(sum0, sum0);
return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_SIMD_H
/* end file simdjson/arm64/simd.h */
/* including simdjson/arm64/stringparsing_defs.h: #include "simdjson/arm64/stringparsing_defs.h" */
/* begin file simdjson/arm64/stringparsing_defs.h */
#ifndef SIMDJSON_ARM64_STRINGPARSING_DEFS_H
#define SIMDJSON_ARM64_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on ARM; therefore, we
// smash them together into a 64-byte mask and get the bitmask from there.
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask64()
};
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_STRINGPARSING_DEFS_H
/* end file simdjson/arm64/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/arm64/begin.h */
/* including simdjson/generic/builder/amalgamated.h for arm64: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for arm64 */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for arm64: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for arm64 */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace arm64 {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(arm64::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<arm64::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace arm64 {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::arm64::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::arm64::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for arm64 */
/* including simdjson/generic/builder/json_builder.h for arm64: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for arm64 */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace arm64 {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace arm64
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return arm64::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return arm64::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
arm64::builder::string_builder b(initial_capacity);
arm64::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for arm64 */
/* including simdjson/generic/builder/fractured_json_builder.h for arm64: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for arm64 */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace arm64 {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace arm64
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return arm64::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = arm64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return arm64::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for arm64 */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for arm64: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for arm64 */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace arm64 {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for arm64 */
/* end file simdjson/generic/builder/amalgamated.h for arm64 */
/* including simdjson/arm64/end.h: #include "simdjson/arm64/end.h" */
/* begin file simdjson/arm64/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "arm64" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/arm64/end.h */
#endif // SIMDJSON_ARM64_BUILDER_H
/* end file simdjson/arm64/builder.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback)
/* including simdjson/fallback/builder.h: #include "simdjson/fallback/builder.h" */
/* begin file simdjson/fallback/builder.h */
#ifndef SIMDJSON_FALLBACK_BUILDER_H
#define SIMDJSON_FALLBACK_BUILDER_H
/* including simdjson/fallback/begin.h: #include "simdjson/fallback/begin.h" */
/* begin file simdjson/fallback/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "fallback" */
#define SIMDJSON_IMPLEMENTATION fallback
/* including simdjson/fallback/base.h: #include "simdjson/fallback/base.h" */
/* begin file simdjson/fallback/base.h */
#ifndef SIMDJSON_FALLBACK_BASE_H
#define SIMDJSON_FALLBACK_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Fallback implementation (runs on any machine).
*/
namespace fallback {
class implementation;
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_BASE_H
/* end file simdjson/fallback/base.h */
/* including simdjson/fallback/bitmanipulation.h: #include "simdjson/fallback/bitmanipulation.h" */
/* begin file simdjson/fallback/bitmanipulation.h */
#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H
#define SIMDJSON_FALLBACK_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace {
#if defined(_MSC_VER) && !defined(_M_ARM64) && !defined(_M_X64)
static inline unsigned char _BitScanForward64(unsigned long* ret, uint64_t x) {
unsigned long x0 = (unsigned long)x, top, bottom;
_BitScanForward(&top, (unsigned long)(x >> 32));
_BitScanForward(&bottom, x0);
*ret = x0 ? bottom : 32 + top;
return x != 0;
}
static unsigned char _BitScanReverse64(unsigned long* ret, uint64_t x) {
unsigned long x1 = (unsigned long)(x >> 32), top, bottom;
_BitScanReverse(&top, x1);
_BitScanReverse(&bottom, (unsigned long)x);
*ret = x1 ? top + 32 : bottom;
return x != 0;
}
#endif
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#ifdef _MSC_VER
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// _MSC_VER
}
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#ifdef _MSC_VER
unsigned long trailing_zero = 0;
// Search the mask data from least significant bit (LSB)
// to most significant bit (MSB) for a set bit (1).
if (_BitScanForward64(&trailing_zero, input_num))
return (int)trailing_zero;
else return 64;
#else
return __builtin_ctzll(input_num);
#endif// _MSC_VER
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_BITMANIPULATION_H
/* end file simdjson/fallback/bitmanipulation.h */
/* including simdjson/fallback/stringparsing_defs.h: #include "simdjson/fallback/stringparsing_defs.h" */
/* begin file simdjson/fallback/stringparsing_defs.h */
#ifndef SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
#define SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace {
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return c == '"'; }
simdjson_inline bool has_backslash() { return c == '\\'; }
simdjson_inline int quote_index() { return c == '"' ? 0 : 1; }
simdjson_inline int backslash_index() { return c == '\\' ? 0 : 1; }
uint8_t c;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// store to dest unconditionally - we can overwrite the bits we don't like later
dst[0] = src[0];
return { src[0] };
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits; }
simdjson_inline int escape_index() { return 0; }
bool escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
dst[0] = src[0];
return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
/* end file simdjson/fallback/stringparsing_defs.h */
/* including simdjson/fallback/numberparsing_defs.h: #include "simdjson/fallback/numberparsing_defs.h" */
/* begin file simdjson/fallback/numberparsing_defs.h */
#ifndef SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
#define SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#ifdef JSON_TEST_NUMBERS // for unit testing
void found_invalid_number(const uint8_t *buf);
void found_integer(int64_t result, const uint8_t *buf);
void found_unsigned_integer(uint64_t result, const uint8_t *buf);
void found_float(double result, const uint8_t *buf);
#endif
namespace simdjson {
namespace fallback {
namespace numberparsing {
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) {
uint64_t val;
memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars));
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace fallback
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
/* end file simdjson/fallback/numberparsing_defs.h */
/* end file simdjson/fallback/begin.h */
/* including simdjson/generic/builder/amalgamated.h for fallback: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for fallback */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for fallback: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for fallback */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace fallback {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(fallback::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<fallback::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace fallback {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::fallback::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::fallback::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for fallback */
/* including simdjson/generic/builder/json_builder.h for fallback: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for fallback */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace fallback {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace fallback
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return fallback::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return fallback::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
fallback::builder::string_builder b(initial_capacity);
fallback::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for fallback */
/* including simdjson/generic/builder/fractured_json_builder.h for fallback: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for fallback */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace fallback {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace fallback
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return fallback::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = fallback::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return fallback::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for fallback */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for fallback: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for fallback */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace fallback {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for fallback */
/* end file simdjson/generic/builder/amalgamated.h for fallback */
/* including simdjson/fallback/end.h: #include "simdjson/fallback/end.h" */
/* begin file simdjson/fallback/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/* undefining SIMDJSON_IMPLEMENTATION from "fallback" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/fallback/end.h */
#endif // SIMDJSON_FALLBACK_BUILDER_H
/* end file simdjson/fallback/builder.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell)
/* including simdjson/haswell/builder.h: #include "simdjson/haswell/builder.h" */
/* begin file simdjson/haswell/builder.h */
#ifndef SIMDJSON_HASWELL_BUILDER_H
#define SIMDJSON_HASWELL_BUILDER_H
/* including simdjson/haswell/begin.h: #include "simdjson/haswell/begin.h" */
/* begin file simdjson/haswell/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "haswell" */
#define SIMDJSON_IMPLEMENTATION haswell
/* including simdjson/haswell/base.h: #include "simdjson/haswell/base.h" */
/* begin file simdjson/haswell/base.h */
#ifndef SIMDJSON_HASWELL_BASE_H
#define SIMDJSON_HASWELL_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL
namespace simdjson {
/**
* Implementation for Haswell (Intel AVX2).
*/
namespace haswell {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BASE_H
/* end file simdjson/haswell/base.h */
/* including simdjson/haswell/intrinsics.h: #include "simdjson/haswell/intrinsics.h" */
/* begin file simdjson/haswell/intrinsics.h */
#ifndef SIMDJSON_HASWELL_INTRINSICS_H
#define SIMDJSON_HASWELL_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
* e.g., if __AVX2__ is set... in turn, we normally set these
* macros by compiling against the corresponding architecture
* (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
* software with these advanced instructions. In simdjson, we
* want to compile the whole program for a generic target,
* and only target our specific kernels. As a workaround,
* we directly include the needed headers. These headers would
* normally guard against such usage, but we carefully included
* <x86intrin.h> (or <intrin.h>) before, so the headers
* are fooled.
*/
#include <bmiintrin.h> // for _blsr_u64
#include <lzcntintrin.h> // for __lzcnt64
#include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
#include <smmintrin.h>
#include <tmmintrin.h>
#include <avxintrin.h>
#include <avx2intrin.h>
#include <wmmintrin.h> // for _mm_clmulepi64_si128
// unfortunately, we may not get _blsr_u64, but, thankfully, clang
// has it as a macro.
#ifndef _blsr_u64
// we roll our own
#define _blsr_u64(n) ((n - 1) & n)
#endif // _blsr_u64
#endif // SIMDJSON_CLANG_VISUAL_STUDIO
static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for haswell kernel.");
#endif // SIMDJSON_HASWELL_INTRINSICS_H
/* end file simdjson/haswell/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
// We enable bmi2 only if LLVM/clang is used, because GCC may not
// make good use of it. See https://github.com/simdjson/simdjson/pull/2243
#if defined(__clang__)
SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt")
#else
SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt")
#endif
#endif
/* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */
/* begin file simdjson/haswell/bitmanipulation.h */
#ifndef SIMDJSON_HASWELL_BITMANIPULATION_H
#define SIMDJSON_HASWELL_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BITMANIPULATION_H
/* end file simdjson/haswell/bitmanipulation.h */
/* including simdjson/haswell/bitmask.h: #include "simdjson/haswell/bitmask.h" */
/* begin file simdjson/haswell/bitmask.h */
#ifndef SIMDJSON_HASWELL_BITMASK_H
#define SIMDJSON_HASWELL_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processor supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BITMASK_H
/* end file simdjson/haswell/bitmask.h */
/* including simdjson/haswell/numberparsing_defs.h: #include "simdjson/haswell/numberparsing_defs.h" */
/* begin file simdjson/haswell/numberparsing_defs.h */
#ifndef SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
#define SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace numberparsing {
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace haswell
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
/* end file simdjson/haswell/numberparsing_defs.h */
/* including simdjson/haswell/simd.h: #include "simdjson/haswell/simd.h" */
/* begin file simdjson/haswell/simd.h */
#ifndef SIMDJSON_HASWELL_SIMD_H
#define SIMDJSON_HASWELL_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m256i value;
// Zero constructor
simdjson_inline base() : value{__m256i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m256i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m256i&() const { return this->value; }
simdjson_inline operator __m256i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm256_or_si256(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm256_and_si256(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm256_xor_si256(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm256_andnot_si256(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint32_t bitmask_t;
typedef uint64_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm256_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm256_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm256_testz_si256(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm256_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm256_setzero_si256(); }
static simdjson_inline simd8<T> load(const T values[32]) {
return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm256_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm256_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm256_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint32_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in four steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second least significant 8 bits
uint8_t mask3 = uint8_t(mask >> 16); // ...
uint8_t mask4 = uint8_t(mask >> 24); // ...
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m256i shufmask = _mm256_set_epi64x(thintable_epi8[mask4], thintable_epi8[mask3],
thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask and so forth
shufmask =
_mm256_add_epi8(shufmask, _mm256_set_epi32(0x18181818, 0x18181818,
0x10101010, 0x10101010, 0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m256i pruned = _mm256_shuffle_epi8(*this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
int pop3 = BitsSetTable256mul2[mask3];
// then load the corresponding mask
// could be done with _mm256_loadu2_m128i but many standard libraries omit this intrinsic.
__m256i v256 = _mm256_castsi128_si256(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8)));
__m256i compactmask = _mm256_insertf128_si256(v256,
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop3 * 8)), 1);
__m256i almostthere = _mm256_shuffle_epi8(pruned, compactmask);
// We just need to write out the result.
// This is the tricky bit that is hard to do
// if we want to return a SIMD register, since there
// is no single-instruction approach to recombine
// the two 128-bit lanes with an offset.
__m128i v128;
v128 = _mm256_castsi256_si128(almostthere);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output), v128);
v128 = _mm256_extractf128_si256(almostthere, 1);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output + 16 - count_ones(mask & 0xFFFF)), v128);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31
) : simd8(_mm256_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm256_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm256_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm256_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm256_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31
) : simd8(_mm256_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm256_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm256_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm256_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm256_min_epu8(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm256_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm256_testz_si256(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm256_testz_si256(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm256_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+32)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint32_t mask1 = uint32_t(mask);
uint32_t mask2 = uint32_t(mask >> 32);
this->chunks[0].compress(mask1, output);
this->chunks[1].compress(mask2, output + 32 - count_ones(mask1));
return 64 - count_ones(mask);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
uint64_t r_hi = this->chunks[1].to_bitmask();
return r_lo | (r_hi << 32);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0] | this->chunks[1];
}
simdjson_inline simd8x64<T> bit_or(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<T>(
this->chunks[0] | mask,
this->chunks[1] | mask
);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_SIMD_H
/* end file simdjson/haswell/simd.h */
/* including simdjson/haswell/stringparsing_defs.h: #include "simdjson/haswell/stringparsing_defs.h" */
/* begin file simdjson/haswell/stringparsing_defs.h */
#ifndef SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
#define SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 15 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
// store to dest unconditionally - we can overwrite the bits we don't like later
v.store(dst);
return {
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
/* end file simdjson/haswell/stringparsing_defs.h */
/* end file simdjson/haswell/begin.h */
/* including simdjson/generic/builder/amalgamated.h for haswell: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for haswell */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for haswell: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for haswell */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace haswell {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(haswell::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<haswell::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace haswell {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::haswell::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::haswell::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for haswell */
/* including simdjson/generic/builder/json_builder.h for haswell: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for haswell */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace haswell {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace haswell
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return haswell::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return haswell::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
haswell::builder::string_builder b(initial_capacity);
haswell::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for haswell */
/* including simdjson/generic/builder/fractured_json_builder.h for haswell: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for haswell */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace haswell {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace haswell
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return haswell::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = haswell::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return haswell::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for haswell */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for haswell: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for haswell */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace haswell {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for haswell */
/* end file simdjson/generic/builder/amalgamated.h for haswell */
/* including simdjson/haswell/end.h: #include "simdjson/haswell/end.h" */
/* begin file simdjson/haswell/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "haswell" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/haswell/end.h */
#endif // SIMDJSON_HASWELL_BUILDER_H
/* end file simdjson/haswell/builder.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake)
/* including simdjson/icelake/builder.h: #include "simdjson/icelake/builder.h" */
/* begin file simdjson/icelake/builder.h */
#ifndef SIMDJSON_ICELAKE_BUILDER_H
#define SIMDJSON_ICELAKE_BUILDER_H
/* including simdjson/icelake/begin.h: #include "simdjson/icelake/begin.h" */
/* begin file simdjson/icelake/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "icelake" */
#define SIMDJSON_IMPLEMENTATION icelake
/* including simdjson/icelake/base.h: #include "simdjson/icelake/base.h" */
/* begin file simdjson/icelake/base.h */
#ifndef SIMDJSON_ICELAKE_BASE_H
#define SIMDJSON_ICELAKE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE
namespace simdjson {
/**
* Implementation for Icelake (Intel AVX512).
*/
namespace icelake {
class implementation;
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BASE_H
/* end file simdjson/icelake/base.h */
/* including simdjson/icelake/intrinsics.h: #include "simdjson/icelake/intrinsics.h" */
/* begin file simdjson/icelake/intrinsics.h */
#ifndef SIMDJSON_ICELAKE_INTRINSICS_H
#define SIMDJSON_ICELAKE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
* e.g., if __AVX2__ is set... in turn, we normally set these
* macros by compiling against the corresponding architecture
* (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
* software with these advanced instructions. In simdjson, we
* want to compile the whole program for a generic target,
* and only target our specific kernels. As a workaround,
* we directly include the needed headers. These headers would
* normally guard against such usage, but we carefully included
* <x86intrin.h> (or <intrin.h>) before, so the headers
* are fooled.
*/
#include <bmiintrin.h> // for _blsr_u64
#include <lzcntintrin.h> // for __lzcnt64
#include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
#include <smmintrin.h>
#include <tmmintrin.h>
#include <avxintrin.h>
#include <avx2intrin.h>
#include <wmmintrin.h> // for _mm_clmulepi64_si128
// Important: we need the AVX-512 headers:
#include <avx512fintrin.h>
#include <avx512dqintrin.h>
#include <avx512cdintrin.h>
#include <avx512bwintrin.h>
#include <avx512vlintrin.h>
#include <avx512vbmiintrin.h>
#include <avx512vbmi2intrin.h>
// unfortunately, we may not get _blsr_u64, but, thankfully, clang
// has it as a macro.
#ifndef _blsr_u64
// we roll our own
#define _blsr_u64(n) ((n - 1) & n)
#endif // _blsr_u64
#endif // SIMDJSON_CLANG_VISUAL_STUDIO
static_assert(sizeof(__m512i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for icelake");
#endif // SIMDJSON_ICELAKE_INTRINSICS_H
/* end file simdjson/icelake/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_ICELAKE
SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,pclmul,lzcnt,popcnt")
#endif
/* including simdjson/icelake/bitmanipulation.h: #include "simdjson/icelake/bitmanipulation.h" */
/* begin file simdjson/icelake/bitmanipulation.h */
#ifndef SIMDJSON_ICELAKE_BITMANIPULATION_H
#define SIMDJSON_ICELAKE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BITMANIPULATION_H
/* end file simdjson/icelake/bitmanipulation.h */
/* including simdjson/icelake/bitmask.h: #include "simdjson/icelake/bitmask.h" */
/* begin file simdjson/icelake/bitmask.h */
#ifndef SIMDJSON_ICELAKE_BITMASK_H
#define SIMDJSON_ICELAKE_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processor supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BITMASK_H
/* end file simdjson/icelake/bitmask.h */
/* including simdjson/icelake/simd.h: #include "simdjson/icelake/simd.h" */
/* begin file simdjson/icelake/simd.h */
#ifndef SIMDJSON_ICELAKE_SIMD_H
#define SIMDJSON_ICELAKE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if defined(__GNUC__) && !defined(__clang__)
#if __GNUC__ == 8
#define SIMDJSON_GCC8 1
#endif // __GNUC__ == 8
#endif // defined(__GNUC__) && !defined(__clang__)
#if SIMDJSON_GCC8
/**
* GCC 8 fails to provide _mm512_set_epi8. We roll our own.
*/
inline __m512i _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) {
return _mm512_set_epi64(uint64_t(a7) + (uint64_t(a6) << 8) + (uint64_t(a5) << 16) + (uint64_t(a4) << 24) + (uint64_t(a3) << 32) + (uint64_t(a2) << 40) + (uint64_t(a1) << 48) + (uint64_t(a0) << 56),
uint64_t(a15) + (uint64_t(a14) << 8) + (uint64_t(a13) << 16) + (uint64_t(a12) << 24) + (uint64_t(a11) << 32) + (uint64_t(a10) << 40) + (uint64_t(a9) << 48) + (uint64_t(a8) << 56),
uint64_t(a23) + (uint64_t(a22) << 8) + (uint64_t(a21) << 16) + (uint64_t(a20) << 24) + (uint64_t(a19) << 32) + (uint64_t(a18) << 40) + (uint64_t(a17) << 48) + (uint64_t(a16) << 56),
uint64_t(a31) + (uint64_t(a30) << 8) + (uint64_t(a29) << 16) + (uint64_t(a28) << 24) + (uint64_t(a27) << 32) + (uint64_t(a26) << 40) + (uint64_t(a25) << 48) + (uint64_t(a24) << 56),
uint64_t(a39) + (uint64_t(a38) << 8) + (uint64_t(a37) << 16) + (uint64_t(a36) << 24) + (uint64_t(a35) << 32) + (uint64_t(a34) << 40) + (uint64_t(a33) << 48) + (uint64_t(a32) << 56),
uint64_t(a47) + (uint64_t(a46) << 8) + (uint64_t(a45) << 16) + (uint64_t(a44) << 24) + (uint64_t(a43) << 32) + (uint64_t(a42) << 40) + (uint64_t(a41) << 48) + (uint64_t(a40) << 56),
uint64_t(a55) + (uint64_t(a54) << 8) + (uint64_t(a53) << 16) + (uint64_t(a52) << 24) + (uint64_t(a51) << 32) + (uint64_t(a50) << 40) + (uint64_t(a49) << 48) + (uint64_t(a48) << 56),
uint64_t(a63) + (uint64_t(a62) << 8) + (uint64_t(a61) << 16) + (uint64_t(a60) << 24) + (uint64_t(a59) << 32) + (uint64_t(a58) << 40) + (uint64_t(a57) << 48) + (uint64_t(a56) << 56));
}
#endif // SIMDJSON_GCC8
namespace simdjson {
namespace icelake {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m512i value;
// Zero constructor
simdjson_inline base() : value{__m512i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m512i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m512i&() const { return this->value; }
simdjson_inline operator __m512i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm512_or_si512(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm512_and_si512(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm512_xor_si512(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm512_andnot_si512(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint32_t bitmask_t;
typedef uint64_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m512i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline uint64_t operator==(const simd8<T> lhs, const simd8<T> rhs) {
return _mm512_cmpeq_epi8_mask(lhs, rhs);
}
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
// workaround for compilers unable to figure out that 16 - N is a constant (GCC 8)
constexpr int shift = 16 - N;
return _mm512_alignr_epi8(*this, _mm512_permutex2var_epi64(prev_chunk, _mm512_set_epi64(13, 12, 11, 10, 9, 8, 7, 6), *this), shift);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m512i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm512_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm512_setzero_si512(); }
static simdjson_inline simd8<T> load(const T values[64]) {
return _mm512_loadu_si512(reinterpret_cast<const __m512i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m512i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm512_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm512_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm512_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 64 - count_ones(mask) bytes of the result are significant but 64 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint64_t mask, L * output) const {
// we deliberately avoid _mm512_mask_compressstoreu_epi8 for portability
// (AMD Zen4 has terrible performance with it, it is effectively broken)
// _mm512_mask_compressstoreu_epi8 (output,~mask,*this);
__m512i compressed = _mm512_maskz_compress_epi8(~mask, *this);
_mm512_storeu_si512(output, compressed); // could use a mask
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m512i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[64]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31,
int8_t v32, int8_t v33, int8_t v34, int8_t v35, int8_t v36, int8_t v37, int8_t v38, int8_t v39,
int8_t v40, int8_t v41, int8_t v42, int8_t v43, int8_t v44, int8_t v45, int8_t v46, int8_t v47,
int8_t v48, int8_t v49, int8_t v50, int8_t v51, int8_t v52, int8_t v53, int8_t v54, int8_t v55,
int8_t v56, int8_t v57, int8_t v58, int8_t v59, int8_t v60, int8_t v61, int8_t v62, int8_t v63
) : simd8(_mm512_set_epi8(
v63, v62, v61, v60, v59, v58, v57, v56,
v55, v54, v53, v52, v51, v50, v49, v48,
v47, v46, v45, v44, v43, v42, v41, v40,
v39, v38, v37, v36, v35, v34, v33, v32,
v31, v30, v29, v28, v27, v26, v25, v24,
v23, v22, v21, v20, v19, v18, v17, v16,
v15, v14, v13, v12, v11, v10, v9, v8,
v7, v6, v5, v4, v3, v2, v1, v0
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm512_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm512_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m512i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[64]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31,
uint8_t v32, uint8_t v33, uint8_t v34, uint8_t v35, uint8_t v36, uint8_t v37, uint8_t v38, uint8_t v39,
uint8_t v40, uint8_t v41, uint8_t v42, uint8_t v43, uint8_t v44, uint8_t v45, uint8_t v46, uint8_t v47,
uint8_t v48, uint8_t v49, uint8_t v50, uint8_t v51, uint8_t v52, uint8_t v53, uint8_t v54, uint8_t v55,
uint8_t v56, uint8_t v57, uint8_t v58, uint8_t v59, uint8_t v60, uint8_t v61, uint8_t v62, uint8_t v63
) : simd8(_mm512_set_epi8(
v63, v62, v61, v60, v59, v58, v57, v56,
v55, v54, v53, v52, v51, v50, v49, v48,
v47, v46, v45, v44, v43, v42, v41, v40,
v39, v38, v37, v36, v35, v34, v33, v32,
v31, v30, v29, v28, v27, v26, v25, v24,
v23, v22, v21, v20, v19, v18, v17, v16,
v15, v14, v13, v12, v11, v10, v9, v8,
v7, v6, v5, v4, v3, v2, v1, v0
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm512_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm512_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm512_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm512_min_epu8(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline uint64_t operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline uint64_t operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm512_movepi8_mask(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const {
return !_mm512_test_epi8_mask(*this, *this);
}
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return !_mm512_test_epi8_mask(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm512_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const simd8<T> chunk0) : chunks{chunk0} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(mask, output);
return 64 - count_ones(mask);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0];
}
simdjson_inline simd8x64<T> bit_or(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<T>(
this->chunks[0] | mask
);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return this->chunks[0] == mask;
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return this->chunks[0] == other.chunks[0];
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return this->chunks[0] <= mask;
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_SIMD_H
/* end file simdjson/icelake/simd.h */
/* including simdjson/icelake/stringparsing_defs.h: #include "simdjson/icelake/stringparsing_defs.h" */
/* begin file simdjson/icelake/stringparsing_defs.h */
#ifndef SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
#define SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint64_t bs_bits;
uint64_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 15 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
// store to dest unconditionally - we can overwrite the bits we don't like later
v.store(dst);
return {
static_cast<uint64_t>(v == '\\'), // bs_bits
static_cast<uint64_t>(v == '"'), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
__mmask64 escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
__mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
__mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
__mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
return {
(is_backslash | is_quote | is_control)
};
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
/* end file simdjson/icelake/stringparsing_defs.h */
/* including simdjson/icelake/numberparsing_defs.h: #include "simdjson/icelake/numberparsing_defs.h" */
/* begin file simdjson/icelake/numberparsing_defs.h */
#ifndef SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
#define SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace numberparsing {
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace icelake
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
/* end file simdjson/icelake/numberparsing_defs.h */
/* end file simdjson/icelake/begin.h */
/* including simdjson/generic/builder/amalgamated.h for icelake: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for icelake */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for icelake: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for icelake */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace icelake {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(icelake::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<icelake::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace icelake {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::icelake::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::icelake::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for icelake */
/* including simdjson/generic/builder/json_builder.h for icelake: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for icelake */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace icelake {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace icelake
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return icelake::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return icelake::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
icelake::builder::string_builder b(initial_capacity);
icelake::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for icelake */
/* including simdjson/generic/builder/fractured_json_builder.h for icelake: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for icelake */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace icelake {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace icelake
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return icelake::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = icelake::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return icelake::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for icelake */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for icelake: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for icelake */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace icelake {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for icelake */
/* end file simdjson/generic/builder/amalgamated.h for icelake */
/* including simdjson/icelake/end.h: #include "simdjson/icelake/end.h" */
/* begin file simdjson/icelake/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_ICELAKE
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "icelake" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/icelake/end.h */
#endif // SIMDJSON_ICELAKE_BUILDER_H
/* end file simdjson/icelake/builder.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64)
/* including simdjson/ppc64/builder.h: #include "simdjson/ppc64/builder.h" */
/* begin file simdjson/ppc64/builder.h */
#ifndef SIMDJSON_PPC64_BUILDER_H
#define SIMDJSON_PPC64_BUILDER_H
/* including simdjson/ppc64/begin.h: #include "simdjson/ppc64/begin.h" */
/* begin file simdjson/ppc64/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "ppc64" */
#define SIMDJSON_IMPLEMENTATION ppc64
/* including simdjson/ppc64/base.h: #include "simdjson/ppc64/base.h" */
/* begin file simdjson/ppc64/base.h */
#ifndef SIMDJSON_PPC64_BASE_H
#define SIMDJSON_PPC64_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for ALTIVEC (PPC64).
*/
namespace ppc64 {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_BASE_H
/* end file simdjson/ppc64/base.h */
/* including simdjson/ppc64/intrinsics.h: #include "simdjson/ppc64/intrinsics.h" */
/* begin file simdjson/ppc64/intrinsics.h */
#ifndef SIMDJSON_PPC64_INTRINSICS_H
#define SIMDJSON_PPC64_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// This should be the correct header whether
// you use visual studio or other compilers.
#include <altivec.h>
// These are defined by altivec.h in GCC toolchain, it is safe to undef them.
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "insufficient padding for ppc64");
#endif // SIMDJSON_PPC64_INTRINSICS_H
/* end file simdjson/ppc64/intrinsics.h */
/* including simdjson/ppc64/bitmanipulation.h: #include "simdjson/ppc64/bitmanipulation.h" */
/* begin file simdjson/ppc64/bitmanipulation.h */
#ifndef SIMDJSON_PPC64_BITMANIPULATION_H
#define SIMDJSON_PPC64_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num - 1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline int count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num); // Visual Studio wants two underscores
}
#else
simdjson_inline int count_ones(uint64_t input_num) {
return __builtin_popcountll(input_num);
}
#endif
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_BITMANIPULATION_H
/* end file simdjson/ppc64/bitmanipulation.h */
/* including simdjson/ppc64/bitmask.h: #include "simdjson/ppc64/bitmask.h" */
/* begin file simdjson/ppc64/bitmask.h */
#ifndef SIMDJSON_PPC64_BITMASK_H
#define SIMDJSON_PPC64_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is
// encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
// You can use the version below, however gcc sometimes miscompiles
// vec_pmsum_be, it happens somewhere around between 8 and 9th version.
// The performance boost was not noticeable, falling back to a usual
// implementation.
// __vector unsigned long long all_ones = {~0ull, ~0ull};
// __vector unsigned long long mask = {bitmask, 0};
// // Clang and GCC return different values for pmsum for ull so cast it to one.
// // Generally it is not specified by ALTIVEC ISA what is returned by
// // vec_pmsum_be.
// #if defined(__LITTLE_ENDIAN__)
// return (uint64_t)(((__vector unsigned long long)vec_pmsum_be(all_ones, mask))[0]);
// #else
// return (uint64_t)(((__vector unsigned long long)vec_pmsum_be(all_ones, mask))[1]);
// #endif
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif
/* end file simdjson/ppc64/bitmask.h */
/* including simdjson/ppc64/numberparsing_defs.h: #include "simdjson/ppc64/numberparsing_defs.h" */
/* begin file simdjson/ppc64/numberparsing_defs.h */
#ifndef SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
#define SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#if defined(__linux__)
#include <byteswap.h>
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#endif
namespace simdjson {
namespace ppc64 {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
#ifdef __BIG_ENDIAN__
#if defined(__linux__)
val = bswap_64(val);
#elif defined(__FreeBSD__)
val = bswap64(val);
#endif
#endif
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace ppc64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
/* end file simdjson/ppc64/numberparsing_defs.h */
/* including simdjson/ppc64/simd.h: #include "simdjson/ppc64/simd.h" */
/* begin file simdjson/ppc64/simd.h */
#ifndef SIMDJSON_PPC64_SIMD_H
#define SIMDJSON_PPC64_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace ppc64 {
namespace {
namespace simd {
using __m128i = __vector unsigned char;
template <typename Child> struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i &() const {
return this->value;
}
simdjson_inline operator __m128i &() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const {
return vec_or(this->value, (__m128i)other);
}
simdjson_inline Child operator&(const Child other) const {
return vec_and(this->value, (__m128i)other);
}
simdjson_inline Child operator^(const Child other) const {
return vec_xor(this->value, (__m128i)other);
}
simdjson_inline Child bit_andnot(const Child other) const {
return vec_andc(this->value, (__m128i)other);
}
simdjson_inline Child &operator|=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast | other;
return *this_cast;
}
simdjson_inline Child &operator&=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast & other;
return *this_cast;
}
simdjson_inline Child &operator^=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast ^ other;
return *this_cast;
}
};
template <typename T, typename Mask = simd8<bool>>
struct base8 : base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) {
return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs);
}
static const int SIZE = sizeof(base<simd8<T>>::value);
template <int N = 1>
simdjson_inline simd8<T> prev(simd8<T> prev_chunk) const {
__m128i chunk = this->value;
#ifdef __LITTLE_ENDIAN__
chunk = (__m128i)vec_reve(this->value);
prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk);
#endif
chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N);
#ifdef __LITTLE_ENDIAN__
chunk = (__m128i)vec_reve((__m128i)chunk);
#endif
return chunk;
}
};
// SIMD byte mask type (returned by things like eq and gt)
template <> struct simd8<bool> : base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) {
return (__m128i)vec_splats((unsigned char)(-(!!_value)));
}
simdjson_inline simd8() : base8<bool>() {}
simdjson_inline simd8(const __m128i _value)
: base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value)
: base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const {
__vector unsigned long long result;
const __m128i perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40,
0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x00};
result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value,
(__m128i)perm_mask));
#ifdef __LITTLE_ENDIAN__
return static_cast<int>(result[1]);
#else
return static_cast<int>(result[0]);
#endif
}
simdjson_inline bool any() const {
return !vec_all_eq(this->value, (__m128i)vec_splats(0));
}
simdjson_inline simd8<bool> operator~() const {
return this->value ^ (__m128i)splat(true);
}
};
template <typename T> struct base8_numeric : base8<T> {
static simdjson_inline simd8<T> splat(T value) {
(void)value;
return (__m128i)vec_splats(value);
}
static simdjson_inline simd8<T> zero() { return splat(0); }
static simdjson_inline simd8<T> load(const T values[16]) {
return (__m128i)(vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(values)));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(T v0, T v1, T v2, T v3, T v4,
T v5, T v6, T v7, T v8, T v9,
T v10, T v11, T v12, T v13,
T v14, T v15) {
return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
v14, v15);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value)
: base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const {
vec_vsx_st(this->value, 0, reinterpret_cast<__m128i *>(dst));
}
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const {
return (__m128i)((__m128i)this->value + (__m128i)other);
}
simdjson_inline simd8<T> operator-(const simd8<T> other) const {
return (__m128i)((__m128i)this->value - (__m128i)other);
}
simdjson_inline simd8<T> &operator+=(const simd8<T> other) {
*this = *this + other;
return *static_cast<simd8<T> *>(this);
}
simdjson_inline simd8<T> &operator-=(const simd8<T> other) {
*this = *this - other;
return *static_cast<simd8<T> *>(this);
}
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior
// for out of range values)
template <typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted
// as a bitset). Passing a 0 value for mask would be equivalent to writing out
// every byte to output. Only the first 16 - count_ones(mask) bytes of the
// result are significant but 16 bytes get written. Design consideration: it
// seems like a function with the signature simd8<L> compress(uint32_t mask)
// would be sensible, but the AVX ISA makes this kind of approach difficult.
template <typename L>
simdjson_inline void compress(uint16_t mask, L *output) const {
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
using internal::thintable_epi8;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
#ifdef __LITTLE_ENDIAN__
__m128i shufmask = (__m128i)(__vector unsigned long long){
thintable_epi8[mask1], thintable_epi8[mask2]};
#else
__m128i shufmask = (__m128i)(__vector unsigned long long){
thintable_epi8[mask2], thintable_epi8[mask1]};
shufmask = (__m128i)vec_reve((__m128i)shufmask);
#endif
// we increment by 0x08 the second half of the mask
shufmask = ((__m128i)shufmask) +
((__m128i)(__vector int){0, 0, 0x08080808, 0x08080808});
// this is the version "nearly pruned"
__m128i pruned = vec_perm(this->value, this->value, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
__m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask);
vec_vsx_st(answer, 0, reinterpret_cast<__m128i *>(output));
}
template <typename L>
simdjson_inline simd8<L>
lookup_16(L replace0, L replace1, L replace2, L replace3, L replace4,
L replace5, L replace6, L replace7, L replace8, L replace9,
L replace10, L replace11, L replace12, L replace13, L replace14,
L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3, replace4, replace5, replace6,
replace7, replace8, replace9, replace10, replace11, replace12,
replace13, replace14, replace15));
}
};
// Signed bytes
template <> struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value)
: base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t *values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(int8_t v0, int8_t v1, int8_t v2, int8_t v3,
int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11,
int8_t v12, int8_t v13, int8_t v14, int8_t v15)
: simd8((__m128i)(__vector signed char){v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10, v11, v12, v13, v14,
v15}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t>
repeat_16(int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5,
int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11,
int8_t v12, int8_t v13, int8_t v14, int8_t v15) {
return simd8<int8_t>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t>
max_val(const simd8<int8_t> other) const {
return (__m128i)vec_max((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<int8_t>
min_val(const simd8<int8_t> other) const {
return (__m128i)vec_min((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<bool>
operator>(const simd8<int8_t> other) const {
return (__m128i)vec_cmpgt((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<bool>
operator<(const simd8<int8_t> other) const {
return (__m128i)vec_cmplt((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
};
// Unsigned bytes
template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value)
: base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t *values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline
simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5,
uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10,
uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15)
: simd8((__m128i){v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t>
repeat_16(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4,
uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9,
uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14,
uint8_t v15) {
return simd8<uint8_t>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15);
}
// Saturated math
simdjson_inline simd8<uint8_t>
saturating_add(const simd8<uint8_t> other) const {
return (__m128i)vec_adds(this->value, (__m128i)other);
}
simdjson_inline simd8<uint8_t>
saturating_sub(const simd8<uint8_t> other) const {
return (__m128i)vec_subs(this->value, (__m128i)other);
}
// Order-specific operations
simdjson_inline simd8<uint8_t>
max_val(const simd8<uint8_t> other) const {
return (__m128i)vec_max(this->value, (__m128i)other);
}
simdjson_inline simd8<uint8_t>
min_val(const simd8<uint8_t> other) const {
return (__m128i)vec_min(this->value, (__m128i)other);
}
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t>
gt_bits(const simd8<uint8_t> other) const {
return this->saturating_sub(other);
}
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t>
lt_bits(const simd8<uint8_t> other) const {
return other.saturating_sub(*this);
}
simdjson_inline simd8<bool>
operator<=(const simd8<uint8_t> other) const {
return other.max_val(*this) == other;
}
simdjson_inline simd8<bool>
operator>=(const simd8<uint8_t> other) const {
return other.min_val(*this) == other;
}
simdjson_inline simd8<bool>
operator>(const simd8<uint8_t> other) const {
return this->gt_bits(other).any_bits_set();
}
simdjson_inline simd8<bool>
operator<(const simd8<uint8_t> other) const {
return this->gt_bits(other).any_bits_set();
}
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const {
return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0)));
}
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const {
return (*this & bits).bits_not_set();
}
simdjson_inline simd8<bool> any_bits_set() const {
return ~this->bits_not_set();
}
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const {
return ~this->bits_not_set(bits);
}
simdjson_inline bool bits_not_set_anywhere() const {
return vec_all_eq(this->value, (__m128i)vec_splats(0));
}
simdjson_inline bool any_bits_set_anywhere() const {
return !bits_not_set_anywhere();
}
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
return vec_all_eq(vec_and(this->value, (__m128i)bits),
(__m128i)vec_splats(0));
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const {
return !bits_not_set_anywhere(bits);
}
template <int N> simdjson_inline simd8<uint8_t> shr() const {
return simd8<uint8_t>(
(__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N)));
}
template <int N> simdjson_inline simd8<uint8_t> shl() const {
return simd8<uint8_t>(
(__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N)));
}
};
template <typename T> struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4,
"PPC64 kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T> &o) = delete; // no copy allowed
simd8x64<T> &
operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1,
const simd8<T> chunk2, const simd8<T> chunk3)
: chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64])
: chunks{simd8<T>::load(ptr), simd8<T>::load(ptr + 16),
simd8<T>::load(ptr + 32), simd8<T>::load(ptr + 48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr + sizeof(simd8<T>) * 0);
this->chunks[1].store(ptr + sizeof(simd8<T>) * 1);
this->chunks[2].store(ptr + sizeof(simd8<T>) * 2);
this->chunks[3].store(ptr + sizeof(simd8<T>) * 3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) |
(this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T *output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16),
output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32),
output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48),
output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
uint64_t r1 = this->chunks[1].to_bitmask();
uint64_t r2 = this->chunks[2].to_bitmask();
uint64_t r3 = this->chunks[3].to_bitmask();
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(this->chunks[0] == mask, this->chunks[1] == mask,
this->chunks[2] == mask, this->chunks[3] == mask)
.to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3])
.to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(this->chunks[0] <= mask, this->chunks[1] <= mask,
this->chunks[2] <= mask, this->chunks[3] <= mask)
.to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_SIMD_INPUT_H
/* end file simdjson/ppc64/simd.h */
/* including simdjson/ppc64/stringparsing_defs.h: #include "simdjson/ppc64/stringparsing_defs.h" */
/* begin file simdjson/ppc64/stringparsing_defs.h */
#ifndef SIMDJSON_PPC64_STRINGPARSING_DEFS_H
#define SIMDJSON_PPC64_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/simd.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote
copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() {
return ((bs_bits - 1) & quote_bits) != 0;
}
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() {
return trailing_zeroes(quote_bits);
}
simdjson_inline int backslash_index() {
return trailing_zeroes(bs_bits);
}
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote
backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1),
"backslash and quote finder must process fewer than "
"SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on
// PPC; therefore, we smash them together into a 64-byte mask and get the
// bitmask from there.
uint64_t bs_and_quote =
simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
// We store it as a 64-bit bitmask even though we only need 16 bits.
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_STRINGPARSING_DEFS_H
/* end file simdjson/ppc64/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/ppc64/begin.h */
/* including simdjson/generic/builder/amalgamated.h for ppc64: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for ppc64 */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for ppc64: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for ppc64 */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace ppc64 {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(ppc64::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<ppc64::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace ppc64 {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::ppc64::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::ppc64::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for ppc64 */
/* including simdjson/generic/builder/json_builder.h for ppc64: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for ppc64 */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace ppc64 {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace ppc64
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return ppc64::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return ppc64::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
ppc64::builder::string_builder b(initial_capacity);
ppc64::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for ppc64 */
/* including simdjson/generic/builder/fractured_json_builder.h for ppc64: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for ppc64 */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace ppc64 {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace ppc64
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return ppc64::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = ppc64::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return ppc64::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for ppc64 */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for ppc64: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for ppc64 */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace ppc64 {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for ppc64 */
/* end file simdjson/generic/builder/amalgamated.h for ppc64 */
/* including simdjson/ppc64/end.h: #include "simdjson/ppc64/end.h" */
/* begin file simdjson/ppc64/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "ppc64" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/ppc64/end.h */
#endif // SIMDJSON_PPC64_BUILDER_H
/* end file simdjson/ppc64/builder.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
/* including simdjson/westmere/builder.h: #include "simdjson/westmere/builder.h" */
/* begin file simdjson/westmere/builder.h */
#ifndef SIMDJSON_WESTMERE_BUILDER_H
#define SIMDJSON_WESTMERE_BUILDER_H
/* including simdjson/westmere/begin.h: #include "simdjson/westmere/begin.h" */
/* begin file simdjson/westmere/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "westmere" */
#define SIMDJSON_IMPLEMENTATION westmere
/* including simdjson/westmere/base.h: #include "simdjson/westmere/base.h" */
/* begin file simdjson/westmere/base.h */
#ifndef SIMDJSON_WESTMERE_BASE_H
#define SIMDJSON_WESTMERE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE
namespace simdjson {
/**
* Implementation for Westmere (Intel SSE4.2).
*/
namespace westmere {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BASE_H
/* end file simdjson/westmere/base.h */
/* including simdjson/westmere/intrinsics.h: #include "simdjson/westmere/intrinsics.h" */
/* begin file simdjson/westmere/intrinsics.h */
#ifndef SIMDJSON_WESTMERE_INTRINSICS_H
#define SIMDJSON_WESTMERE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
*/
#include <smmintrin.h> // for _mm_alignr_epi8
#include <wmmintrin.h> // for _mm_clmulepi64_si128
#endif
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere");
#endif // SIMDJSON_WESTMERE_INTRINSICS_H
/* end file simdjson/westmere/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE
SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt")
#endif
/* including simdjson/westmere/bitmanipulation.h: #include "simdjson/westmere/bitmanipulation.h" */
/* begin file simdjson/westmere/bitmanipulation.h */
#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H
#define SIMDJSON_WESTMERE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H
/* end file simdjson/westmere/bitmanipulation.h */
/* including simdjson/westmere/bitmask.h: #include "simdjson/westmere/bitmask.h" */
/* begin file simdjson/westmere/bitmask.h */
#ifndef SIMDJSON_WESTMERE_BITMASK_H
#define SIMDJSON_WESTMERE_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processing supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMASK_H
/* end file simdjson/westmere/bitmask.h */
/* including simdjson/westmere/numberparsing_defs.h: #include "simdjson/westmere/numberparsing_defs.h" */
/* begin file simdjson/westmere/numberparsing_defs.h */
#ifndef SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
#define SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
/* including simdjson/westmere/base.h: #include "simdjson/westmere/base.h" */
/* begin file simdjson/westmere/base.h */
#ifndef SIMDJSON_WESTMERE_BASE_H
#define SIMDJSON_WESTMERE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE
namespace simdjson {
/**
* Implementation for Westmere (Intel SSE4.2).
*/
namespace westmere {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BASE_H
/* end file simdjson/westmere/base.h */
/* including simdjson/westmere/intrinsics.h: #include "simdjson/westmere/intrinsics.h" */
/* begin file simdjson/westmere/intrinsics.h */
#ifndef SIMDJSON_WESTMERE_INTRINSICS_H
#define SIMDJSON_WESTMERE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
*/
#include <smmintrin.h> // for _mm_alignr_epi8
#include <wmmintrin.h> // for _mm_clmulepi64_si128
#endif
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere");
#endif // SIMDJSON_WESTMERE_INTRINSICS_H
/* end file simdjson/westmere/intrinsics.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace numberparsing {
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace westmere
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
/* end file simdjson/westmere/numberparsing_defs.h */
/* including simdjson/westmere/simd.h: #include "simdjson/westmere/simd.h" */
/* begin file simdjson/westmere/simd.h */
#ifndef SIMDJSON_WESTMERE_SIMD_H
#define SIMDJSON_WESTMERE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
namespace simd {
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm_setzero_si128(); }
static simdjson_inline simd8<T> load(const T values[16]) {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m128i shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask
shufmask =
_mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m128i pruned = _mm_shuffle_epi8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8));
__m128i answer = _mm_shuffle_epi8(pruned, compactmask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm_min_epu8(*this, other); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm_testz_si128(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask() );
uint64_t r1 = this->chunks[1].to_bitmask() ;
uint64_t r2 = this->chunks[2].to_bitmask() ;
uint64_t r3 = this->chunks[3].to_bitmask() ;
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H
/* end file simdjson/westmere/simd.h */
/* including simdjson/westmere/stringparsing_defs.h: #include "simdjson/westmere/stringparsing_defs.h" */
/* begin file simdjson/westmere/stringparsing_defs.h */
#ifndef SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
#define SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
/* including simdjson/westmere/bitmanipulation.h: #include "simdjson/westmere/bitmanipulation.h" */
/* begin file simdjson/westmere/bitmanipulation.h */
#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H
#define SIMDJSON_WESTMERE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H
/* end file simdjson/westmere/bitmanipulation.h */
/* including simdjson/westmere/simd.h: #include "simdjson/westmere/simd.h" */
/* begin file simdjson/westmere/simd.h */
#ifndef SIMDJSON_WESTMERE_SIMD_H
#define SIMDJSON_WESTMERE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
namespace simd {
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm_setzero_si128(); }
static simdjson_inline simd8<T> load(const T values[16]) {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m128i shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask
shufmask =
_mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m128i pruned = _mm_shuffle_epi8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8));
__m128i answer = _mm_shuffle_epi8(pruned, compactmask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm_min_epu8(*this, other); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm_testz_si128(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask() );
uint64_t r1 = this->chunks[1].to_bitmask() ;
uint64_t r2 = this->chunks[2].to_bitmask() ;
uint64_t r3 = this->chunks[3].to_bitmask() ;
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H
/* end file simdjson/westmere/simd.h */
namespace simdjson {
namespace westmere {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + 16);
v0.store(dst);
v1.store(dst + 16);
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
/* end file simdjson/westmere/stringparsing_defs.h */
/* end file simdjson/westmere/begin.h */
/* including simdjson/generic/builder/amalgamated.h for westmere: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for westmere */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for westmere: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for westmere */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace westmere {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(westmere::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<westmere::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace westmere {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::westmere::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::westmere::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for westmere */
/* including simdjson/generic/builder/json_builder.h for westmere: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for westmere */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace westmere {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace westmere
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return westmere::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return westmere::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
westmere::builder::string_builder b(initial_capacity);
westmere::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for westmere */
/* including simdjson/generic/builder/fractured_json_builder.h for westmere: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for westmere */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace westmere {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace westmere
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return westmere::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = westmere::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return westmere::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for westmere */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for westmere: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for westmere */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace westmere {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for westmere */
/* end file simdjson/generic/builder/amalgamated.h for westmere */
/* including simdjson/westmere/end.h: #include "simdjson/westmere/end.h" */
/* begin file simdjson/westmere/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "westmere" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/westmere/end.h */
#endif // SIMDJSON_WESTMERE_BUILDER_H
/* end file simdjson/westmere/builder.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
/* including simdjson/lsx/builder.h: #include "simdjson/lsx/builder.h" */
/* begin file simdjson/lsx/builder.h */
#ifndef SIMDJSON_LSX_BUILDER_H
#define SIMDJSON_LSX_BUILDER_H
/* including simdjson/lsx/begin.h: #include "simdjson/lsx/begin.h" */
/* begin file simdjson/lsx/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "lsx" */
#define SIMDJSON_IMPLEMENTATION lsx
/* including simdjson/lsx/base.h: #include "simdjson/lsx/base.h" */
/* begin file simdjson/lsx/base.h */
#ifndef SIMDJSON_LSX_BASE_H
#define SIMDJSON_LSX_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for LSX.
*/
namespace lsx {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_BASE_H
/* end file simdjson/lsx/base.h */
/* including simdjson/lsx/intrinsics.h: #include "simdjson/lsx/intrinsics.h" */
/* begin file simdjson/lsx/intrinsics.h */
#ifndef SIMDJSON_LSX_INTRINSICS_H
#define SIMDJSON_LSX_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <lsxintrin.h>
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch SX");
#endif // SIMDJSON_LSX_INTRINSICS_H
/* end file simdjson/lsx/intrinsics.h */
/* including simdjson/lsx/bitmanipulation.h: #include "simdjson/lsx/bitmanipulation.h" */
/* begin file simdjson/lsx/bitmanipulation.h */
#ifndef SIMDJSON_LSX_BITMANIPULATION_H
#define SIMDJSON_LSX_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
return __lsx_vpickve2gr_w(__lsx_vpcnt_d(__m128i(v2u64{input_num, 0})), 0);
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_BITMANIPULATION_H
/* end file simdjson/lsx/bitmanipulation.h */
/* including simdjson/lsx/bitmask.h: #include "simdjson/lsx/bitmask.h" */
/* begin file simdjson/lsx/bitmask.h */
#ifndef SIMDJSON_LSX_BITMASK_H
#define SIMDJSON_LSX_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif
/* end file simdjson/lsx/bitmask.h */
/* including simdjson/lsx/numberparsing_defs.h: #include "simdjson/lsx/numberparsing_defs.h" */
/* begin file simdjson/lsx/numberparsing_defs.h */
#ifndef SIMDJSON_LSX_NUMBERPARSING_DEFS_H
#define SIMDJSON_LSX_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace lsx {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace lsx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H
/* end file simdjson/lsx/numberparsing_defs.h */
/* including simdjson/lsx/simd.h: #include "simdjson/lsx/simd.h" */
/* begin file simdjson/lsx/simd.h */
#ifndef SIMDJSON_LSX_SIMD_H
#define SIMDJSON_LSX_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
simdjson_inline operator const v16i8&() const { return (v16i8&)this->value; }
simdjson_inline operator v16i8&() { return (v16i8&)this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return __lsx_vor_v(*this, other); }
simdjson_inline Child operator&(const Child other) const { return __lsx_vand_v(*this, other); }
simdjson_inline Child operator^(const Child other) const { return __lsx_vxor_v(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return __lsx_vandn_v(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return __lsx_vseq_b(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return __lsx_vor_v(__lsx_vbsll_v(*this, N), __lsx_vbsrl_v(prev_chunk, 16 - N));
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) {
return __lsx_vreplgr2vr_b(uint8_t(-(!!_value)));
}
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return __lsx_vpickve2gr_w(__lsx_vmskltz_b(*this), 0); }
simdjson_inline bool any() const { return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(*this), 0); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return __lsx_vreplgr2vr_b(_value); }
static simdjson_inline simd8<T> zero() { return __lsx_vldi(0); }
static simdjson_inline simd8<T> load(const T values[16]) {
return __lsx_vld(reinterpret_cast<const __m128i *>(values), 0);
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const {
return __lsx_vst(*this, reinterpret_cast<__m128i *>(dst), 0);
}
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return __lsx_vadd_b(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return __lsx_vsub_b(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __lsx_vshuf_b(lookup_table, lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by haswell
// lsx do it in 2 steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second least significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register.
__m128i shufmask = {int64_t(thintable_epi8[mask1]), int64_t(thintable_epi8[mask2]) + 0x0808080808080808};
// this is the version "nearly pruned"
__m128i pruned = __lsx_vshuf_b(*this, *this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask
__m128i compactmask = __lsx_vldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop1 * 8);
__m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask);
__lsx_vst(answer, reinterpret_cast<uint8_t*>(output), 0);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[16]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8({
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return __lsx_vmax_b(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return __lsx_vmin_b(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return __lsx_vslt_b(other, *this); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return __lsx_vslt_b(*this, other); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[16]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(__m128i(v16u8{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
})) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __lsx_vsadd_bu(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __lsx_vssub_bu(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return __lsx_vmax_bu(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return __lsx_vmin_bu(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return 0 == __lsx_vpickve2gr_w(__lsx_vmskltz_b(*this), 0); }
simdjson_inline bool bits_not_set_anywhere() const { return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(*this), 0); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0);
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(__lsx_vsrli_b(*this, N)); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(__lsx_vslli_b(*this, N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint16_t mask1 = uint16_t(mask);
uint16_t mask2 = uint16_t(mask >> 16);
uint16_t mask3 = uint16_t(mask >> 32);
uint16_t mask4 = uint16_t(mask >> 48);
__m128i zcnt = __lsx_vpcnt_h(__m128i(v2u64{~mask, 0}));
uint64_t zcnt1 = __lsx_vpickve2gr_hu(zcnt, 0);
uint64_t zcnt2 = __lsx_vpickve2gr_hu(zcnt, 1);
uint64_t zcnt3 = __lsx_vpickve2gr_hu(zcnt, 2);
uint64_t zcnt4 = __lsx_vpickve2gr_hu(zcnt, 3);
uint8_t *voutput = reinterpret_cast<uint8_t*>(output);
// There should be a critical value which processes in scaler is faster.
if (zcnt1)
this->chunks[0].compress(mask1, reinterpret_cast<T*>(voutput));
voutput += zcnt1;
if (zcnt2)
this->chunks[1].compress(mask2, reinterpret_cast<T*>(voutput));
voutput += zcnt2;
if (zcnt3)
this->chunks[2].compress(mask3, reinterpret_cast<T*>(voutput));
voutput += zcnt3;
if (zcnt4)
this->chunks[3].compress(mask4, reinterpret_cast<T*>(voutput));
voutput += zcnt4;
return reinterpret_cast<uint64_t>(voutput) - reinterpret_cast<uint64_t>(output);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline uint64_t to_bitmask() const {
__m128i mask1 = __lsx_vmskltz_b(this->chunks[0]);
__m128i mask2 = __lsx_vmskltz_b(this->chunks[1]);
__m128i mask3 = __lsx_vmskltz_b(this->chunks[2]);
__m128i mask4 = __lsx_vmskltz_b(this->chunks[3]);
mask1 = __lsx_vilvl_h(mask2, mask1);
mask2 = __lsx_vilvl_h(mask4, mask3);
return __lsx_vpickve2gr_du(__lsx_vilvl_w(mask2, mask1), 0);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_SIMD_H
/* end file simdjson/lsx/simd.h */
/* including simdjson/lsx/stringparsing_defs.h: #include "simdjson/lsx/stringparsing_defs.h" */
/* begin file simdjson/lsx/stringparsing_defs.h */
#ifndef SIMDJSON_LSX_STRINGPARSING_DEFS_H
#define SIMDJSON_LSX_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on LSX; therefore, we
// smash them together into a 64-byte mask and get the bitmask from there.
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_STRINGPARSING_DEFS_H
/* end file simdjson/lsx/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/lsx/begin.h */
/* including simdjson/generic/builder/amalgamated.h for lsx: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for lsx */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for lsx: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for lsx */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace lsx {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(lsx::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<lsx::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace lsx {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::lsx::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::lsx::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for lsx */
/* including simdjson/generic/builder/json_builder.h for lsx: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for lsx */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace lsx {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace lsx
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return lsx::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return lsx::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
lsx::builder::string_builder b(initial_capacity);
lsx::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for lsx */
/* including simdjson/generic/builder/fractured_json_builder.h for lsx: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for lsx */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace lsx {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace lsx
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return lsx::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = lsx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return lsx::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for lsx */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for lsx: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for lsx */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace lsx {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for lsx */
/* end file simdjson/generic/builder/amalgamated.h for lsx */
/* including simdjson/lsx/end.h: #include "simdjson/lsx/end.h" */
/* begin file simdjson/lsx/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "lsx" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/lsx/end.h */
#endif // SIMDJSON_LSX_BUILDER_H
/* end file simdjson/lsx/builder.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
/* including simdjson/lasx/builder.h: #include "simdjson/lasx/builder.h" */
/* begin file simdjson/lasx/builder.h */
#ifndef SIMDJSON_LASX_BUILDER_H
#define SIMDJSON_LASX_BUILDER_H
/* including simdjson/lasx/begin.h: #include "simdjson/lasx/begin.h" */
/* begin file simdjson/lasx/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "lasx" */
#define SIMDJSON_IMPLEMENTATION lasx
#include <lsxintrin.h> // This is a hack. We should not need to put this include here.
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
// nothing needed.
#else
SIMDJSON_TARGET_REGION("lasx,lsx")
#endif
/* including simdjson/lasx/base.h: #include "simdjson/lasx/base.h" */
/* begin file simdjson/lasx/base.h */
#ifndef SIMDJSON_LASX_BASE_H
#define SIMDJSON_LASX_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for LASX.
*/
namespace lasx {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_BASE_H
/* end file simdjson/lasx/base.h */
/* including simdjson/lasx/intrinsics.h: #include "simdjson/lasx/intrinsics.h" */
/* begin file simdjson/lasx/intrinsics.h */
#ifndef SIMDJSON_LASX_INTRINSICS_H
#define SIMDJSON_LASX_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <lsxintrin.h>
#include <lasxintrin.h>
static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch ASX");
#endif // SIMDJSON_LASX_INTRINSICS_H
/* end file simdjson/lasx/intrinsics.h */
/* including simdjson/lasx/bitmanipulation.h: #include "simdjson/lasx/bitmanipulation.h" */
/* begin file simdjson/lasx/bitmanipulation.h */
#ifndef SIMDJSON_LASX_BITMANIPULATION_H
#define SIMDJSON_LASX_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
return __lasx_xvpickve2gr_w(__lasx_xvpcnt_d(__m256i(v4u64{input_num, 0, 0, 0})), 0);
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_BITMANIPULATION_H
/* end file simdjson/lasx/bitmanipulation.h */
/* including simdjson/lasx/bitmask.h: #include "simdjson/lasx/bitmask.h" */
/* begin file simdjson/lasx/bitmask.h */
#ifndef SIMDJSON_LASX_BITMASK_H
#define SIMDJSON_LASX_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif
/* end file simdjson/lasx/bitmask.h */
/* including simdjson/lasx/numberparsing_defs.h: #include "simdjson/lasx/numberparsing_defs.h" */
/* begin file simdjson/lasx/numberparsing_defs.h */
#ifndef SIMDJSON_LASX_NUMBERPARSING_DEFS_H
#define SIMDJSON_LASX_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace lasx {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace lasx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H
/* end file simdjson/lasx/numberparsing_defs.h */
/* including simdjson/lasx/simd.h: #include "simdjson/lasx/simd.h" */
/* begin file simdjson/lasx/simd.h */
#ifndef SIMDJSON_LASX_SIMD_H
#define SIMDJSON_LASX_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m256i value;
// Zero constructor
simdjson_inline base() : value{__m256i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m256i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m256i&() const { return this->value; }
simdjson_inline operator __m256i&() { return this->value; }
simdjson_inline operator const v32i8&() const { return (v32i8&)this->value; }
simdjson_inline operator v32i8&() { return (v32i8&)this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return __lasx_xvor_v(*this, other); }
simdjson_inline Child operator&(const Child other) const { return __lasx_xvand_v(*this, other); }
simdjson_inline Child operator^(const Child other) const { return __lasx_xvxor_v(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return __lasx_xvandn_v(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return __lasx_xvseq_b(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
__m256i hi = __lasx_xvbsll_v(*this, N);
__m256i lo = __lasx_xvbsrl_v(*this, 16 - N);
__m256i tmp = __lasx_xvbsrl_v(prev_chunk, 16 - N);
lo = __lasx_xvpermi_q(lo, tmp, 0x21);
return __lasx_xvor_v(hi, lo);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const {
__m256i mask = __lasx_xvmskltz_b(*this);
return (__lasx_xvpickve2gr_w(mask, 4) << 16) | (__lasx_xvpickve2gr_w(mask, 0));
}
simdjson_inline bool any() const {
__m256i v = __lasx_xvmsknz_b(*this);
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) {
return __lasx_xvreplgr2vr_b(_value);
}
static simdjson_inline simd8<T> zero() { return __lasx_xvldi(0); }
static simdjson_inline simd8<T> load(const T values[32]) {
return __lasx_xvld(reinterpret_cast<const __m256i *>(values), 0);
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[32]) const {
return __lasx_xvst(*this, reinterpret_cast<__m256i *>(dst), 0);
}
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return __lasx_xvadd_b(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return __lasx_xvsub_b(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __lasx_xvshuf_b(lookup_table, lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
template<typename L>
simdjson_inline void compress(uint32_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by haswell
// lasx do it in 4 steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second significant 8 bits
uint8_t mask3 = uint8_t(mask >> 16); // ...
uint8_t mask4 = uint8_t(mask >> 24); // ...
// next line just loads the 64-bit values thintable_epi8[mask{1,2,3,4}]
// into a 256-bit register.
__m256i shufmask = {int64_t(thintable_epi8[mask1]), int64_t(thintable_epi8[mask2]) + 0x0808080808080808, int64_t(thintable_epi8[mask3]), int64_t(thintable_epi8[mask4]) + 0x0808080808080808};
// this is the version "nearly pruned"
__m256i pruned = __lasx_xvshuf_b(*this, *this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
int pop2 = BitsSetTable256mul2[mask2];
int pop3 = BitsSetTable256mul2[mask3];
// then load the corresponding mask
__m256i masklo = __lasx_xvldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop1 * 8);
__m256i maskhi = __lasx_xvldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop3 * 8);
__m256i compactmask = __lasx_xvpermi_q(maskhi, masklo, 0x20);
__m256i answer = __lasx_xvshuf_b(pruned, pruned, compactmask);
__lasx_xvst(answer, reinterpret_cast<uint8_t*>(output), 0);
uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2);
uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3);
uint64_t *pos = reinterpret_cast<uint64_t*>(reinterpret_cast<uint8_t*>(output) + 16 - (pop1 + pop2) / 2);
pos[0] = value3;
pos[1] = value4;
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31
) : simd8({
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return __lasx_xvmax_b(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return __lasx_xvmin_b(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return __lasx_xvslt_b(other, *this); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return __lasx_xvslt_b(*this, other); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31
) : simd8(__m256i(v32u8{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
})) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __lasx_xvsadd_bu(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __lasx_xvssub_bu(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return __lasx_xvmax_bu(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return __lasx_xvmin_bu(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const {
__m256i mask = __lasx_xvmskltz_b(*this);
return (0 == __lasx_xvpickve2gr_w(mask, 0)) && (0 == __lasx_xvpickve2gr_w(mask, 4));
}
simdjson_inline bool bits_not_set_anywhere() const {
__m256i v = __lasx_xvmsknz_b(*this);
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
__m256i v = __lasx_xvmsknz_b(__lasx_xvand_v(*this, bits));
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(__lasx_xvsrli_b(*this, N)); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(__lasx_xvslli_b(*this, N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+32)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint32_t mask1 = uint32_t(mask);
uint32_t mask2 = uint32_t(mask >> 32);
__m256i zcnt = __lasx_xvpcnt_w(__m256i(v4u64{~mask, 0, 0, 0}));
uint64_t zcnt1 = __lasx_xvpickve2gr_wu(zcnt, 0);
uint64_t zcnt2 = __lasx_xvpickve2gr_wu(zcnt, 1);
// There should be a critical value which processes in scaler is faster.
if (zcnt1)
this->chunks[0].compress(mask1, output);
if (zcnt2)
this->chunks[1].compress(mask2, output + zcnt1);
return zcnt1 + zcnt2;
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
}
simdjson_inline uint64_t to_bitmask() const {
__m256i mask0 = __lasx_xvmskltz_b(this->chunks[0]);
__m256i mask1 = __lasx_xvmskltz_b(this->chunks[1]);
__m256i mask_tmp = __lasx_xvpickve_w(mask0, 4);
__m256i tmp = __lasx_xvpickve_w(mask1, 4);
mask0 = __lasx_xvinsve0_w(mask0, mask1, 1);
mask_tmp = __lasx_xvinsve0_w(mask_tmp, tmp, 1);
return __lasx_xvpickve2gr_du(__lasx_xvpackev_h(mask_tmp, mask0), 0);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0] | this->chunks[1];
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_SIMD_H
/* end file simdjson/lasx/simd.h */
/* including simdjson/lasx/stringparsing_defs.h: #include "simdjson/lasx/stringparsing_defs.h" */
/* begin file simdjson/lasx/stringparsing_defs.h */
#ifndef SIMDJSON_LASX_STRINGPARSING_DEFS_H
#define SIMDJSON_LASX_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return {
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_STRINGPARSING_DEFS_H
/* end file simdjson/lasx/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/lasx/begin.h */
/* including simdjson/generic/builder/amalgamated.h for lasx: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for lasx */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for lasx: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for lasx */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace lasx {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(lasx::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<lasx::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace lasx {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::lasx::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::lasx::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for lasx */
/* including simdjson/generic/builder/json_builder.h for lasx: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for lasx */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace lasx {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace lasx
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return lasx::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return lasx::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
lasx::builder::string_builder b(initial_capacity);
lasx::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for lasx */
/* including simdjson/generic/builder/fractured_json_builder.h for lasx: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for lasx */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace lasx {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace lasx
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return lasx::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = lasx::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return lasx::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for lasx */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for lasx: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for lasx */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace lasx {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for lasx */
/* end file simdjson/generic/builder/amalgamated.h for lasx */
/* including simdjson/lasx/end.h: #include "simdjson/lasx/end.h" */
/* begin file simdjson/lasx/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "lasx" */
#undef SIMDJSON_IMPLEMENTATION
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
// nothing needed.
#else
SIMDJSON_UNTARGET_REGION
#endif
/* end file simdjson/lasx/end.h */
#endif // SIMDJSON_LASX_BUILDER_H
/* end file simdjson/lasx/builder.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
/* including simdjson/rvv-vls/builder.h: #include "simdjson/rvv-vls/builder.h" */
/* begin file simdjson/rvv-vls/builder.h */
#ifndef SIMDJSON_RVV_VLS_BUILDER_H
#define SIMDJSON_RVV_VLS_BUILDER_H
/* including simdjson/rvv-vls/begin.h: #include "simdjson/rvv-vls/begin.h" */
/* begin file simdjson/rvv-vls/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "rvv_vls" */
#define SIMDJSON_IMPLEMENTATION rvv_vls
/* including simdjson/rvv-vls/base.h: #include "simdjson/rvv-vls/base.h" */
/* begin file simdjson/rvv-vls/base.h */
#ifndef SIMDJSON_RVV_VLS_BASE_H
#define SIMDJSON_RVV_VLS_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* RVV-VLS implementation.
*/
namespace rvv_vls {
class implementation;
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BASE_H
/* end file simdjson/rvv-vls/base.h */
/* including simdjson/rvv-vls/intrinsics.h: #include "simdjson/rvv-vls/intrinsics.h" */
/* begin file simdjson/rvv-vls/intrinsics.h */
#ifndef SIMDJSON_RVV_VLS_INTRINSICS_H
#define SIMDJSON_RVV_VLS_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <riscv_vector.h>
#define simdutf_vrgather_u8m1x2(tbl, idx) \
__riscv_vcreate_v_u8m1_u8m2( \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 0), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 1), \
__riscv_vsetvlmax_e8m1()))
#define simdutf_vrgather_u8m1x4(tbl, idx) \
__riscv_vcreate_v_u8m1_u8m4( \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 0), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 1), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 2), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 3), \
__riscv_vsetvlmax_e8m1()))
#if __riscv_zbc
#include <riscv_bitmanip.h>
#endif
#endif // SIMDJSON_RVV_VLS_INTRINSICS_H
/* end file simdjson/rvv-vls/intrinsics.h */
/* including simdjson/rvv-vls/bitmanipulation.h: #include "simdjson/rvv-vls/bitmanipulation.h" */
/* begin file simdjson/rvv-vls/bitmanipulation.h */
#ifndef SIMDJSON_RVV_VLS_BITMANIPULATION_H
#define SIMDJSON_RVV_VLS_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
simdjson_inline long long int count_ones(uint64_t input_num) {
return __builtin_popcountll(input_num);
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BITMANIPULATION_H
/* end file simdjson/rvv-vls/bitmanipulation.h */
/* including simdjson/rvv-vls/bitmask.h: #include "simdjson/rvv-vls/bitmask.h" */
/* begin file simdjson/rvv-vls/bitmask.h */
#ifndef SIMDJSON_RVV_VLS_BITMASK_H
#define SIMDJSON_RVV_VLS_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
#if __riscv_zbc
return __riscv_clmul_64(bitmask, ~(uint64_t)0);
#elif __riscv_zvbc
return __riscv_vmv_x(__riscv_vclmul(__riscv_vmv_s_x_u64m1(bitmask, 1), ~(uint64_t)0, 1));
#else
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
#endif
return bitmask;
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BITMASK_H
/* end file simdjson/rvv-vls/bitmask.h */
/* including simdjson/rvv-vls/simd.h: #include "simdjson/rvv-vls/simd.h" */
/* begin file simdjson/rvv-vls/simd.h */
#ifndef SIMDJSON_RVV_VLS_SIMD_H
#define SIMDJSON_RVV_VLS_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
namespace simd {
#if __riscv_v_fixed_vlen >= 512
static constexpr size_t VL8 = 512/8;
using vint8_t = vint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vuint8_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vbool_t = vbool8_t __attribute__((riscv_rvv_vector_bits(512/8)));
using vbitmask_t = uint64_t;
#else
static constexpr size_t VL8 = __riscv_v_fixed_vlen/8;
using vint8_t = vint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
using vuint8_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
using vbool_t = vbool8_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen/8)));
#if __riscv_v_fixed_vlen == 128
using vbitmask_t = uint16_t;
#elif __riscv_v_fixed_vlen == 256
using vbitmask_t = uint32_t;
#endif
#endif
#if __riscv_v_fixed_vlen == 128
using vuint8x64_t = vuint8m4_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool2_t __attribute__((riscv_rvv_vector_bits(512/8)));
#elif __riscv_v_fixed_vlen == 256
using vuint8x64_t = vuint8m2_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool4_t __attribute__((riscv_rvv_vector_bits(512/8)));
#else
using vuint8x64_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool8_t __attribute__((riscv_rvv_vector_bits(512/8)));
#endif
template<typename T>
struct simd8;
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool> {
vbool_t value;
using bitmask_t = vbitmask_t;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vbool_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(__riscv_vmclr_m_b8(VL8)) {}
simdjson_inline simd8(bool _value) : simd8(splat(_value)) {}
simdjson_inline operator const vbool_t&() const { return value; }
simdjson_inline operator vbool_t&() { return value; }
static simdjson_inline simd8<bool> splat(bool _value) {
return __riscv_vreinterpret_b8(__riscv_vmv_v_x_u64m1(((uint64_t)!_value)-1, 1));
}
simdjson_inline vbitmask_t to_bitmask() const {
#if __riscv_v_fixed_vlen == 128
return __riscv_vmv_x(__riscv_vreinterpret_u16m1(value));
#elif __riscv_v_fixed_vlen == 256
return __riscv_vmv_x(__riscv_vreinterpret_u32m1(value));
#else
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(value));
#endif
}
// Bit operations
simdjson_inline simd8<bool> operator|(const simd8<bool> other) const { return __riscv_vmor(*this, other, VL8); }
simdjson_inline simd8<bool> operator&(const simd8<bool> other) const { return __riscv_vmand(*this, other, VL8); }
simdjson_inline simd8<bool> operator^(const simd8<bool> other) const { return __riscv_vmxor(*this, other, VL8); }
simdjson_inline simd8<bool> bit_andnot(const simd8<bool> other) const { return __riscv_vmandn(other, *this, VL8); }
simdjson_inline simd8<bool> operator~() const { return __riscv_vmnot(*this, VL8); }
simdjson_inline simd8<bool>& operator|=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline simd8<bool>& operator&=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline simd8<bool>& operator^=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Unsigned bytes
template<>
struct simd8<uint8_t> {
vuint8_t value;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vuint8_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(zero()) {}
simdjson_inline simd8(const uint8_t values[VL8]) : simd8(load(values)) {}
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdjson_inline simd8(simd8<bool> mask) : value(__riscv_vmerge_vxm_u8m1(zero(), -1, (vbool_t)mask, VL8)) {}
simdjson_inline operator const vuint8_t&() const { return this->value; }
simdjson_inline operator vuint8_t&() { return this->value; }
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(vuint8_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
static simdjson_inline vuint8_t splat(uint8_t _value) { return __riscv_vmv_v_x_u8m1(_value, VL8); }
static simdjson_inline vuint8_t zero() { return splat(0); }
static simdjson_inline vuint8_t load(const uint8_t values[VL8]) { return __riscv_vle8_v_u8m1(values, VL8); }
// Bit operations
simdjson_inline simd8<uint8_t> operator|(const simd8<uint8_t> other) const { return __riscv_vor_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator&(const simd8<uint8_t> other) const { return __riscv_vand_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator^(const simd8<uint8_t> other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator~() const { return __riscv_vnot_v_u8m1(value, VL8); }
#if __riscv_zvbb
simdjson_inline simd8<uint8_t> bit_andnot(const simd8<uint8_t> other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); }
#else
simdjson_inline simd8<uint8_t> bit_andnot(const simd8<uint8_t> other) const { return other & ~*this; }
#endif
simdjson_inline simd8<uint8_t>& operator|=(const simd8<uint8_t> other) { value = *this | other; return *this; }
simdjson_inline simd8<uint8_t>& operator&=(const simd8<uint8_t> other) { value = *this & other; return *this; }
simdjson_inline simd8<uint8_t>& operator^=(const simd8<uint8_t> other) { value = *this ^ other; return *this; }
simdjson_inline simd8<bool> operator==(const simd8<uint8_t> other) const { return __riscv_vmseq(value, other, VL8); }
simdjson_inline simd8<bool> operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); }
template<int N=1>
simdjson_inline simd8<uint8_t> prev(const simd8<uint8_t> prev_chunk) const {
return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8);
}
// Store to array
simdjson_inline void store(uint8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); }
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __riscv_vsaddu(value, other, VL8); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __riscv_vssubu(value, other, VL8); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<uint8_t> operator+(const simd8<uint8_t> other) const { return __riscv_vadd(value, other, VL8); }
simdjson_inline simd8<uint8_t> operator-(const simd8<uint8_t> other) const { return __riscv_vsub(value, other, VL8); }
simdjson_inline simd8<uint8_t>& operator+=(const simd8<uint8_t> other) { value = *this + other; return *this; }
simdjson_inline simd8<uint8_t>& operator-=(const simd8<uint8_t> other) { value = *this - other; return *this; }
// Order-specific operations
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return __riscv_vmsleu(value, other, VL8); }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return __riscv_vmsgeu(value, other, VL8); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return __riscv_vmsltu(value, other, VL8); }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return __riscv_vmsgtu(value, other, VL8); }
// Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero.
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this > other); }
// Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero.
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this < other); }
// Bit-specific operations
simdjson_inline bool any_bits_set_anywhere() const {
return __riscv_vfirst(__riscv_vmsne(value, 0, VL8), VL8) >= 0;
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return __riscv_vsrl(value, N, VL8); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return __riscv_vsll(value, N, VL8); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __riscv_vrgather(lookup_table, value, VL8);
}
// compress inactive elements, to match AVX-512 behavior
template<typename L>
simdjson_inline void compress(vbitmask_t mask, L * output) const {
mask = (vbitmask_t)~mask;
#if __riscv_v_fixed_vlen == 128
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u16m1(mask, 1));
#elif __riscv_v_fixed_vlen == 256
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u32m1(mask, 1));
#else
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u64m1(mask, 1));
#endif
__riscv_vse8_v_u8m1(output, __riscv_vcompress(value, m, VL8), count_ones(mask));
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> {
vint8_t value;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vint8_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(zero()) {}
simdjson_inline simd8(const int8_t values[VL8]) : simd8(load(values)) {}
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
simdjson_inline operator const vint8_t&() const { return this->value; }
simdjson_inline operator vint8_t&() { return this->value; }
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(vint8_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
static simdjson_inline vint8_t splat(int8_t _value) { return __riscv_vmv_v_x_i8m1(_value, VL8); }
static simdjson_inline vint8_t zero() { return splat(0); }
static simdjson_inline vint8_t load(const int8_t values[VL8]) { return __riscv_vle8_v_i8m1(values, VL8); }
simdjson_inline void store(int8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); }
// Explicit conversion to/from unsigned
simdjson_inline explicit simd8(const vuint8_t other): simd8(__riscv_vreinterpret_i8m1(other)) {}
simdjson_inline explicit operator simd8<uint8_t>() const { return __riscv_vreinterpret_u8m1(value); }
// Math
simdjson_inline simd8<int8_t> operator+(const simd8<int8_t> other) const { return __riscv_vadd(value, other, VL8); }
simdjson_inline simd8<int8_t> operator-(const simd8<int8_t> other) const { return __riscv_vsub(value, other, VL8); }
simdjson_inline simd8<int8_t>& operator+=(const simd8<int8_t> other) { value = *this + other; return *this; }
simdjson_inline simd8<int8_t>& operator-=(const simd8<int8_t> other) { value = *this - other; return *this; }
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val( const simd8<int8_t> other) const { return __riscv_vmax( value, other, VL8); }
simdjson_inline simd8<int8_t> min_val( const simd8<int8_t> other) const { return __riscv_vmin( value, other, VL8); }
simdjson_inline simd8<bool> operator>( const simd8<int8_t> other) const { return __riscv_vmsgt(value, other, VL8); }
simdjson_inline simd8<bool> operator<( const simd8<int8_t> other) const { return __riscv_vmslt(value, other, VL8); }
simdjson_inline simd8<bool> operator==(const simd8<int8_t> other) const { return __riscv_vmseq(value, other, VL8); }
template<int N=1>
simdjson_inline simd8<int8_t> prev(const simd8<int8_t> prev_chunk) const {
return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8);
}
// Perform a lookup assuming no value is larger than 16
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __riscv_vrgather(lookup_table, value, VL8);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
template<typename T>
struct simd8x64;
template<>
struct simd8x64<uint8_t> {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<uint8_t>);
vuint8x64_t value;
#if __riscv_v_fixed_vlen >= 512
template<int idx> simd8<uint8_t> get() const { return value; }
#else
template<int idx> simd8<uint8_t> get() const { return __riscv_vget_u8m1(value, idx); }
#endif
simdjson_inline operator const vuint8x64_t&() const { return this->value; }
simdjson_inline operator vuint8x64_t&() { return this->value; }
simd8x64(const simd8x64<uint8_t>& o) = delete; // no copy allowed
simd8x64<uint8_t>& operator=(const simd8<uint8_t>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
#if __riscv_v_fixed_vlen == 128
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m4(ptr, n)) {}
#elif __riscv_v_fixed_vlen == 256
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m2(ptr, n)) {}
#else
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m1(ptr, n)) {}
#endif
simdjson_inline void store(uint8_t ptr[64]) const {
__riscv_vse8(ptr, value, 64);
}
simdjson_inline bool is_ascii() const {
#if __riscv_v_fixed_vlen == 128
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m4(value), 0, 64), 64) < 0;
#elif __riscv_v_fixed_vlen == 256
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m2(value), 0, 64), 64) < 0;
#else
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m1(value), 0, 64), 64) < 0;
#endif
}
// compress inactive elements, to match AVX-512 behavior
simdjson_inline uint64_t compress(uint64_t mask, uint8_t * output) const {
mask = ~mask;
#if __riscv_v_fixed_vlen == 128
vboolx64_t m = __riscv_vreinterpret_b2(__riscv_vmv_s_x_u64m1(mask, 1));
#elif __riscv_v_fixed_vlen == 256
vboolx64_t m = __riscv_vreinterpret_b4(__riscv_vmv_s_x_u64m1(mask, 1));
#else
vboolx64_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u64m1(mask, 1));
#endif
size_t cnt = count_ones(mask);
__riscv_vse8(output, __riscv_vcompress(value, m, 64), cnt);
return cnt;
}
simdjson_inline uint64_t eq(const uint8_t m) const {
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vmseq(value, m, 64)));
}
simdjson_inline uint64_t lteq(const uint8_t m) const {
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vmsleu(value, m, 64)));
}
}; // struct simd8x64<uint8_t>
} // namespace simd
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_SIMD_H
/* end file simdjson/rvv-vls/simd.h */
/* including simdjson/rvv-vls/stringparsing_defs.h: #include "simdjson/rvv-vls/stringparsing_defs.h" */
/* begin file simdjson/rvv-vls/stringparsing_defs.h */
#ifndef SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
#define SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint64_t bs_bits;
uint64_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() };
}
struct escaping {
static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return { ((v == '"') | (v == '\\') | (v == 32)).to_bitmask() };
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
/* end file simdjson/rvv-vls/stringparsing_defs.h */
/* including simdjson/rvv-vls/numberparsing_defs.h: #include "simdjson/rvv-vls/numberparsing_defs.h" */
/* begin file simdjson/rvv-vls/numberparsing_defs.h */
#ifndef SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
#define SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#ifdef JSON_TEST_NUMBERS // for unit testing
void found_invalid_number(const uint8_t *buf);
void found_integer(int64_t result, const uint8_t *buf);
void found_unsigned_integer(uint64_t result, const uint8_t *buf);
void found_float(double result, const uint8_t *buf);
#endif
namespace simdjson {
namespace rvv_vls {
namespace numberparsing {
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) {
uint64_t val;
#if __riscv_misaligned_fast
memcpy(&val, chars, sizeof(uint64_t));
#else
val = __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vlmul_ext_u8m1(__riscv_vle8_v_u8mf2((uint8_t*)chars, 8))));
#endif
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars));
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace rvv_vls
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
/* end file simdjson/rvv-vls/numberparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/rvv-vls/begin.h */
/* including simdjson/generic/builder/amalgamated.h for rvv_vls: #include "simdjson/generic/builder/amalgamated.h" */
/* begin file simdjson/generic/builder/amalgamated.h for rvv_vls */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#error simdjson/generic/builder/dependencies.h must be included before simdjson/generic/builder/amalgamated.h!
#endif
/* including simdjson/generic/builder/json_string_builder.h for rvv_vls: #include "simdjson/generic/builder/json_string_builder.h" */
/* begin file simdjson/generic/builder/json_string_builder.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace rvv_vls {
namespace builder {
class string_builder;
}}
template <typename T, typename = void>
struct has_custom_serialization : std::false_type {};
inline constexpr struct serialize_tag {
template <typename T>
constexpr void operator()(rvv_vls::builder::string_builder& b, T&& obj) const{
return tag_invoke(*this, b, std::forward<T>(obj));
}
} serialize{};
template <typename T>
struct has_custom_serialization<T, std::void_t<
decltype(tag_invoke(serialize, std::declval<rvv_vls::builder::string_builder&>(), std::declval<T&>()))
>> : std::true_type {};
template <typename T>
constexpr bool require_custom_serialization = has_custom_serialization<T>::value;
#else
struct has_custom_serialization : std::false_type {};
#endif // SIMDJSON_SUPPORTS_CONCEPTS
namespace rvv_vls {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class can support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = DEFAULT_INITIAL_CAPACITY);
static constexpr size_t DEFAULT_INITIAL_CAPACITY = 1024;
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key>
simdjson_inline void escape_and_append_with_quotes() noexcept;
#endif
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(char input) noexcept;
/**
* Append the character surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(const char* input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append "{" to the buffer.
*/
simdjson_inline void start_object() noexcept;
/**
* Append "}" to the buffer.
*/
simdjson_inline void end_object() noexcept;
/**
* Append "[" to the buffer.
*/
simdjson_inline void start_array() noexcept;
/**
* Append "]" to the buffer.
*/
simdjson_inline void end_array() noexcept;
/**
* Append "," to the buffer.
*/
simdjson_inline void append_comma() noexcept;
/**
* Append ":" to the buffer.
*/
simdjson_inline void append_colon() noexcept;
/**
* Append a key-value pair to the buffer.
* The key is escaped and surrounded by double quotes.
* The value is escaped if it is a string.
*/
template<typename key_type, typename value_type>
simdjson_inline void append_key_value(key_type key, value_type value) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template<constevalutil::fixed_string key, typename value_type>
simdjson_inline void append_key_value(value_type value) noexcept;
template<constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void append_key_value() noexcept;
template<constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void append_key_value() noexcept;
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(const T &opt);
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void append(T &&val);
// Support for string-like types
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char*>::value )
simdjson_inline void append(const T &value);
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires (!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void append(const R &range) noexcept;
#endif
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false) simdjson_lifetime_bound;
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
// ============================================================
// Internal hooks for the position-as-local writer in json_builder.h.
// These exist so the reflection atom code can hold buffer pointer,
// position and capacity in registers across long write chains rather
// than reloading them after every char* write (strict aliasing
// forces those reloads when accessed via members of *this). User
// code should NOT call these directly.
// ============================================================
simdjson_inline char *unsafe_data() noexcept { return buffer.get(); }
simdjson_inline size_t unsafe_position() const noexcept { return position; }
simdjson_inline size_t unsafe_capacity() const noexcept { return capacity; }
simdjson_inline void unsafe_set_position(size_t p) noexcept { position = p; }
/// Make capacity available for at least `n` more bytes after the current
/// position. Returns false if the allocation failed.
simdjson_inline bool unsafe_grow(size_t needed_total_capacity) noexcept {
grow_buffer(needed_total_capacity);
return is_valid;
}
simdjson_inline bool unsafe_is_valid() const noexcept { return is_valid; }
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
/**
* Append constevalutil::static_string<Text>. The whole zero-padded array is
* copied in 16-byte blocks, but position only advances by the text length.
*/
template <typename Text>
simdjson_inline void append_static_string() noexcept;
#endif
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
#if !SIMDJSON_STATIC_REFLECTION
// fallback implementation until we have static reflection
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = simdjson::rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::rvv_vls::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view s;
auto e = b.view().get(s);
if(e) { return e; }
return std::string(s);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = simdjson::rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
simdjson::rvv_vls::builder::string_builder b(initial_capacity);
b.append(z);
std::string_view sv;
auto e = b.view().get(sv);
if(e) { return e; }
s.assign(sv.data(), sv.size());
return simdjson::SUCCESS;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_SUPPORTS_CONCEPTS
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_H
/* end file simdjson/generic/builder/json_string_builder.h for rvv_vls */
/* including simdjson/generic/builder/json_builder.h for rvv_vls: #include "simdjson/generic/builder/json_builder.h" */
/* begin file simdjson/generic/builder/json_builder.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/concepts.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
#include <charconv>
#include <cstring>
#include <meta>
#include <memory>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
// #include <static_reflection> // for std::define_static_string - header not available yet
namespace simdjson {
namespace rvv_vls {
namespace builder {
// Forward-declare helpers defined in json_string_builder-inl.h so the
// writer-based atom code below can call them (the -inl.h is not yet
// included at the point this header is parsed; without these forwards,
// name lookup falls back to the wrong outer namespace).
namespace internal {
simdjson_really_inline char *write_uint_jeaiii(char *p, uint64_t v) noexcept;
simdjson_inline char *write_double(char *p, double v) noexcept;
} // namespace internal
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out);
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_GCC_WARNING(-Warray-bounds)
#if !defined(__clang__)
SIMDJSON_DISABLE_GCC_WARNING(-Wstringop-overflow)
#endif
// =============================================================
// `writer`: position-as-local hot-path writer used by the reflection
// atom code below. Holds the buffer pointer, write position and
// capacity in three fields that, once `writer` itself is a stack-local
// in the caller and all atom() functions are inlined, become true
// register-resident locals after SROA. Glaze achieves the same effect
// by passing `B&& b, auto&& ix` through every helper. Holding `pos`
// in a register (rather than as a member of string_builder) is what
// breaks the strict-aliasing penalty on every char* write through the
// buffer, which forces a reload of `b.position` and `b.capacity`
// after every byte.
//
// basic_writer<false> (below) is the unchecked variant: the caller has
// already reserved enough capacity for everything the write chain can
// produce (see bound_detail::size_bound), so ensure() compiles away.
// =============================================================
template <bool Checked>
struct basic_writer {
static constexpr bool checked = Checked;
char *ptr; // buffer pointer (refreshed after a grow)
size_t pos; // write position (local)
size_t cap; // capacity (refreshed after a grow)
string_builder &sb; // back-ref for grow / sync
// Snapshot string_builder state into a writer for the duration of
// a write chain.
simdjson_really_inline basic_writer(string_builder &builder) noexcept
: ptr(builder.unsafe_data())
, pos(builder.unsafe_position())
, cap(builder.unsafe_capacity())
, sb(builder) {}
// Write the local position back to the underlying string_builder.
// Caller is responsible for invoking before the writer is dropped
// (otherwise data is lost). Idempotent.
simdjson_really_inline void sync() noexcept {
sb.unsafe_set_position(pos);
}
// Ensure at least `n` more bytes of free capacity. Grows the
// underlying buffer if needed (rare path). Returns false on
// allocation failure.
simdjson_really_inline bool ensure(size_t n) noexcept {
// pos <= cap, and cap is the size of a live allocation, so pos + n
// cannot wrap when n is a small constant or a compile-time length.
// Callers passing a size derived from input (the string atoms) must
// bound it against pos themselves. Keep the `pos + n <= cap` form:
// `n <= cap - pos` is measurably slower once the serializer is inlined.
if (simdjson_likely(pos + n <= cap)) { return true; }
return grow_slow(n);
}
simdjson_never_inline bool grow_slow(size_t n) noexcept {
// Detect overflow.
// This is pedantic except maybe on 32-bit targets.
if (simdjson_unlikely(pos + n < pos)) return false;
sb.unsafe_set_position(pos);
// even if 2*capacity overflows, the (std::max) below will pick the needed value,
// so we do not need a separate overflow check here.
if (!sb.unsafe_grow((std::max)(cap * 2, pos + n))) {
// The string_builder freed its buffer and is now invalid (null buffer,
// zero capacity and position). Mirror that state so that every later
// ensure() fails too: callers only return from the current atom, and
// their callers keep writing.
ptr = nullptr;
pos = 0;
cap = 0;
return false;
}
ptr = sb.unsafe_data();
cap = sb.unsafe_capacity();
return true;
}
};
// The unchecked writer writes into a raw buffer that the caller sized with
// serialized_size_bound: it never grows and needs no string_builder.
template <>
struct basic_writer<false> {
static constexpr bool checked = false;
char *ptr;
size_t pos;
simdjson_really_inline basic_writer(char *buffer, size_t position) noexcept
: ptr(buffer), pos(position) {}
simdjson_really_inline bool ensure(size_t) const noexcept { return true; }
};
using writer = basic_writer<true>;
using unchecked_writer = basic_writer<false>;
// Bytes reserved past the size bound for an unchecked writer: it may then
// write a little past the end of what it produces (e.g., copy keys as whole
// 16-byte blocks).
inline constexpr size_t unchecked_slack = 64;
// === Helper: invoke a string_builder member that writes variable-length
// content (escape_and_append_with_quotes etc), syncing the writer's local
// state before the call and reloading after. Used for string fields where
// rewriting the entire SIMD escape path through the writer would be a much
// bigger refactor. f may be user code (a with<Adapter> serializer) that
// throws: the exception then propagates to the caller.
template <class W, class F>
simdjson_really_inline void call_through_string_builder(W &w, F &&f) noexcept(noexcept(f(w.sb))) {
w.sync();
f(w.sb);
w.ptr = w.sb.unsafe_data();
w.pos = w.sb.unsafe_position();
w.cap = w.sb.unsafe_capacity();
}
// Helpers implementing the serialization side of the annotations (see
// simdjson/annotations.h) for reflected structures.
namespace annotation_detail {
// A member is serialized unless it is annotated with skip or skip_serializing.
consteval bool is_serialized_member(std::meta::info dm) {
return !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(dm, ^^simdjson::detail::skip_serializing_tag);
}
// False when the member has a skip_serializing_if<pred> annotation and
// pred(value) is true.
template <auto dm, typename V>
simdjson_really_inline bool should_serialize(const V &value) {
constexpr std::meta::info skip_if_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::skip_serializing_if_t);
if constexpr (skip_if_type != std::meta::info{}) {
using skip_if = typename [: skip_if_type :];
return !skip_if::predicate(value);
} else {
(void)value;
return true;
}
}
// Serialize a member value, through its with<Adapter> annotation when the
// adapter provides a serialize function.
template <auto dm, class W, typename V>
simdjson_really_inline void atom_member(W &w, const V &value) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
if constexpr (requires(string_builder &b) { adapter::serialize(b, value); }) {
call_through_string_builder(w, [&](string_builder &b) { adapter::serialize(b, value); });
} else {
atom(w, value);
}
} else {
atom(w, value);
}
}
// Write the "key":value pairs of the members of t (without the braces), each
// preceded by a comma unless it is the first one. The members of a member
// annotated with flatten are written in its place.
template <class W, class T>
simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) {
// Per-field block: ensure key+value worst case, then write key + value
// through the writer's local pos. For arithmetic fields, the integer
// write happens directly via write_uint_jeaiii on w.ptr+w.pos, so pos
// never round-trips through memory.
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (is_serialized_member(dm)) {
if (should_serialize<dm>(t.[:dm:])) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
static_assert(std::meta::is_class_type(simdjson::detail::flattened_type(dm)));
using flattened = std::remove_cvref_t<decltype(t.[:dm:])>;
static_assert(!concepts::container_but_not_string<flattened> && !concepts::string_view_keyed_map<flattened> &&
!concepts::appendable_containers<flattened> && !concepts::optional_type<flattened> &&
!concepts::smart_pointer<flattened> && !std::is_same_v<flattened, std::string> &&
!std::is_same_v<flattened, std::string_view> && !require_custom_serialization<flattened>,
"simdjson::flatten requires a member whose type is a structure serialized member by member");
atom_fields(w, t.[:dm:], first);
} else {
// Copy the key as whole 16-byte blocks from a zero-padded copy (one
// load and one store); ensure() reserves the padded length, and the
// unchecked writer has slack past its bound. Prior related work:
// jsonifier copies a power-of-two padded key and advances the cursor
// by the real length (serialize_impl.hpp, packed_blitter,
// https://github.com/nihilai-collective/Jsonifier).
constexpr const char* key_name = simdjson::get_json_key_name<dm>();
constexpr size_t first_key_len = constevalutil::consteval_to_quoted_escaped(key_name).size() + 1;
constexpr size_t rest_key_len = first_key_len + 1;
constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0'));
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" +
std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0'));
if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len));
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len));
w.pos += rest_key_len;
}
first = false;
atom_member<dm>(w, t.[:dm:]);
}
}
}
};
}
} // namespace annotation_detail
template <class W, class T>
requires(concepts::container_but_not_string<T> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
auto it = t.begin();
auto end = t.end();
if (it == end) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
atom(w, *it);
++it;
for (; it != end; ++it) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
atom(w, *it);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
template <class W, class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
// Inline the escape path through the writer so we never round-trip
// pos through memory for string fields (Twitter is dominated by
// these -- sync/reload around each string was a real cost).
std::string_view input;
if constexpr (std::is_same_v<T, char>) {
input = std::string_view(&t, 1);
} else {
input = std::string_view(t);
}
// Worst-case escape: every byte expands to \uXXXX (6 chars), plus 2 quotes.
// Guard against w.pos + 2 + 6 * input.size() wrapping for huge inputs -- if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(input.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * input.size())) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(input, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
}
template <class W, concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &m) {
if (m.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "{}", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '{';
bool first = true;
for (const auto& [key, value] : m) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
// Keys must be convertible to string_view per the concept.
std::string_view key_sv(key);
// Guard against w.pos + 3 + 6 * key_sv.size() wrapping for huge keys, if
// it wrapped to a small value, ensure() would spuriously succeed and the
// subsequent escape would overflow the buffer. max - w.pos cannot wrap, and
// size < (max - pos) / 6 implies pos + 6 * size + 6 <= max.
// Note that this is pedantic except maybe on 32-bit targets.
if constexpr (W::checked) {
if (simdjson_unlikely(key_sv.size() >= ((std::numeric_limits<size_t>::max)() - w.pos) / 6)) { return; }
if (!w.ensure(2 + 6 * key_sv.size() + 1)) { return; }
}
w.ptr[w.pos++] = '"';
w.pos += write_string_escaped(key_sv, w.ptr + w.pos);
w.ptr[w.pos++] = '"';
w.ptr[w.pos++] = ':';
atom(w, value);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '}';
}
template<class W, typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value && !std::is_same_v<number_type, char>>::type>
simdjson_really_inline constexpr void atom(W &w, const number_type t) {
// Booleans / floats: defer to string_builder (rare path; keeps writer hot
// path free of float-formatter machinery). For integers, write directly
// via jeaiii using local pos.
if constexpr (std::is_same_v<number_type, bool>) {
if (t) {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "true", 4);
w.pos += 4;
} else {
if (!w.ensure(5)) return;
std::memcpy(w.ptr + w.pos, "false", 5);
w.pos += 5;
}
} else if constexpr (std::is_floating_point_v<number_type>) {
if constexpr (W::checked) {
call_through_string_builder(w, [&](string_builder &b) { b.append(t); });
} else {
w.pos = size_t(internal::write_double(w.ptr + w.pos, double(t)) - w.ptr);
}
} else if constexpr (std::is_unsigned_v<number_type>) {
if (!w.ensure(20)) return;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(t));
w.pos = static_cast<size_t>(end - w.ptr);
} else {
// signed integral
if (!w.ensure(20)) return;
using U = typename std::make_unsigned<number_type>::type;
bool negative = t < 0;
U pv = negative ? U(0) - static_cast<U>(t) : static_cast<U>(t);
w.ptr[w.pos] = '-';
w.pos += negative;
char *end = internal::write_uint_jeaiii(
w.ptr + w.pos, static_cast<uint64_t>(pv));
w.pos = static_cast<size_t>(end - w.ptr);
}
}
template <class W, class T>
requires(std::is_class_v<T> && !concepts::container_but_not_string<T> &&
!concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> &&
!concepts::smart_pointer<T> &&
!concepts::appendable_containers<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> &&
!std::is_same_v<T, const char*> &&
!std::is_same_v<T, char> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &t) {
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
// A transparent structure is serialized as its single member.
constexpr auto dm = simdjson::detail::transparent_member(^^T);
annotation_detail::atom_member<dm>(w, t.[:dm:]);
} else {
bool first = true;
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '{';
annotation_detail::atom_fields(w, t, first);
if (!w.ensure(1)) { return; }
w.ptr[w.pos++] = '}';
}
}
// Support for optional types (std::optional, etc.)
template <class W, concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &opt) {
if (opt) {
atom(w, opt.value());
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for smart pointers (std::unique_ptr, std::shared_ptr, etc.)
template <class W, concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &ptr) {
if (ptr) {
atom(w, *ptr);
} else {
if (!w.ensure(4)) return;
std::memcpy(w.ptr + w.pos, "null", 4);
w.pos += 4;
}
}
// Support for enums - serialize as string representation using expand approach from P2996R12
template <class W, typename T>
requires(std::is_enum_v<T> && !require_custom_serialization<T>)
simdjson_really_inline void atom(W &w, const T &e) {
#if SIMDJSON_STATIC_REFLECTION
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
constexpr auto enum_str = std::define_static_string(constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>()));
constexpr size_t enum_str_len = std::char_traits<char>::length(enum_str);
if (e == [:enum_val:]) {
if (!w.ensure(enum_str_len)) return;
std::memcpy(w.ptr + w.pos, enum_str, enum_str_len);
w.pos += enum_str_len;
return;
}
};
// Fallback to integer if enum value not found
atom(w, static_cast<std::underlying_type_t<T>>(e));
#else
// Fallback: serialize as integer if reflection not available
atom(w, static_cast<std::underlying_type_t<T>>(e));
#endif
}
// Support for appendable containers that don't have operator[] (sets, etc.)
template <class W, concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_really_inline constexpr void atom(W &w, const T &container) {
if (container.empty()) {
if (!w.ensure(2)) return;
std::memcpy(w.ptr + w.pos, "[]", 2);
w.pos += 2;
return;
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = '[';
bool first = true;
for (const auto& item : container) {
if (!first) {
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ',';
}
first = false;
atom(w, item);
}
if (!w.ensure(1)) return;
w.ptr[w.pos++] = ']';
}
// =============================================================
// Size bound: an upper bound on the number of bytes that atom(w, t) writes.
// Computing it first lets append() reserve the capacity once and then run
// the whole write chain through an unchecked_writer, without a capacity
// check before every write. It mirrors the atom() overloads above.
//
// Prior related work: jsonifier sizes the document first, counting 6 bytes
// per string byte, resizes once to that bound plus slack, and writes with
// no capacity check on each store. to_json() does that resize through
// std::string::resize_and_overwrite. See serializer.hpp and
// serialize_impl.hpp in https://github.com/nihilai-collective/Jsonifier
// and https://nihilai-collective.net/serialization.
// =============================================================
namespace bound_detail {
// Whether size_bound covers everything that atom() writes for T: not when a
// member is serialized by a with<Adapter> serializer, which writes an unknown
// amount through the string_builder.
template <class T>
consteval bool is_bounded() {
if constexpr (require_custom_serialization<T>) {
return false;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> || std::is_arithmetic_v<T> || std::is_enum_v<T>) {
return true;
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return is_bounded<std::remove_cvref_t<decltype(*std::declval<const T &>())>>();
} else if constexpr (concepts::string_view_keyed_map<T>) {
return is_bounded<std::remove_cvref_t<typename T::mapped_type>>();
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
return is_bounded<std::remove_cvref_t<std::ranges::range_value_t<T>>>();
} else {
bool bounded = true;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
bounded = bounded && simdjson::detail::annotation_of_template(dm, ^^simdjson::detail::with_t) == std::meta::info{} &&
is_bounded<std::remove_cvref_t<decltype(std::declval<const T &>().[:dm:])>>();
}
};
return bounded;
}
}
template <class T>
consteval size_t enum_bound() {
size_t bound = 20; // the integer fallback
template for (constexpr auto enum_val : std::define_static_array(std::meta::enumerators_of(^^T))) {
constexpr size_t len = std::char_traits<char>::length(std::define_static_string(
constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<enum_val>())));
bound = (std::max)(bound, len);
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound(const T &t) noexcept;
// Bound for the "key":value pairs of a structure, commas included.
template <class T>
simdjson_really_inline size_t fields_bound(const T &t) noexcept {
size_t bound = 0;
template for (constexpr auto dm : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (annotation_detail::is_serialized_member(dm)) {
if constexpr (simdjson::detail::has_annotation(dm, ^^simdjson::detail::flatten_tag)) {
bound += fields_bound(t.[:dm:]);
} else {
constexpr size_t rest_key_len = constevalutil::consteval_to_quoted_escaped(simdjson::get_json_key_name<dm>()).size() + 2;
bound += rest_key_len + size_bound(t.[:dm:]);
}
}
};
return bound;
}
template <class T>
simdjson_really_inline size_t size_bound([[maybe_unused]] const T &t) noexcept {
if constexpr (std::is_same_v<T, char>) {
return 2 + 6;
} else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *>) {
// Every byte may become \uXXXX, plus the quotes.
return 2 + 6 * std::string_view(t).size();
} else if constexpr (std::is_same_v<T, bool>) {
return 5;
} else if constexpr (std::is_floating_point_v<T>) {
return simdjson::internal::to_chars_buffer_size;
} else if constexpr (std::is_arithmetic_v<T>) {
return 20;
} else if constexpr (std::is_enum_v<T>) {
return enum_bound<T>();
} else if constexpr (concepts::optional_type<T> || concepts::smart_pointer<T>) {
return t ? size_bound(*t) : 4;
} else if constexpr (concepts::string_view_keyed_map<T>) {
size_t bound = 2;
for (const auto &[key, value] : t) {
// comma, quotes, colon
bound += 4 + 6 * std::string_view(key).size() + size_bound(value);
}
return bound;
} else if constexpr (concepts::container_but_not_string<T> || concepts::appendable_containers<T>) {
using value_type = std::remove_cvref_t<std::ranges::range_value_t<T>>;
if constexpr (std::is_arithmetic_v<value_type> && !std::is_same_v<value_type, char>) {
// A fixed bound per element: no need to visit them.
return 2 + size_t(std::ranges::distance(t)) * (1 + size_bound(value_type{}));
} else {
size_t bound = 2;
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC novector // a vector loop is slower on short containers
#endif
#pragma GCC unroll 4
for (const auto &item : t) {
bound += 1 + size_bound(item);
}
return bound;
}
} else if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag)) {
constexpr auto dm = simdjson::detail::transparent_member(^^T);
return size_bound(t.[:dm:]);
} else {
return 2 + fields_bound(t);
}
}
} // namespace bound_detail
// Write t through an unchecked writer when its size bound is available,
// reserving that many bytes first, and through the checked writer otherwise.
template <class T>
simdjson_really_inline void append_bounded(string_builder &b, const T &t) {
// On 32-bit systems, the bound could overflow: keep the checked writer.
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<T>()) {
const size_t bound = bound_detail::size_bound(t) + unchecked_slack;
const size_t pos = b.unsafe_position();
// The bound is a sum of in-memory sizes times a small constant: it cannot
// overflow on a 64-bit system. Be pedantic elsewhere.
if (sizeof(size_t) >= 8 || bound <= (std::numeric_limits<size_t>::max)() - pos) {
const size_t cap = b.unsafe_capacity();
// Grow geometrically so that many small appends stay amortized.
if (pos + bound <= cap || b.unsafe_grow((std::max)(cap * 2, pos + bound))) {
unchecked_writer w(b.unsafe_data(), pos);
atom(w, t);
b.unsafe_set_position(w.pos);
}
return;
}
}
writer w(b);
atom(w, t);
w.sync();
}
// append() -- top-level entry. Each overload constructs a stack-local
// writer, runs atom(w, t) through the inlined call chain, then syncs
// the local position back into the string_builder.
template <class T>
requires(std::is_arithmetic_v<T> && !std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <class T>
requires(std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
std::is_same_v<T, const char *> ||
std::is_same_v<T, char>)
simdjson_inline void append(string_builder &b, const T &t) {
writer w(b);
atom(w, t);
w.sync();
}
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::smart_pointer T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::appendable_containers T>
requires(!concepts::container_but_not_string<T> && !concepts::string_view_keyed_map<T> &&
!concepts::optional_type<T> && !concepts::smart_pointer<T> &&
!std::is_same_v<T, std::string> &&
!std::is_same_v<T, std::string_view> && !std::is_same_v<T, const char*> && !require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
template <concepts::string_view_keyed_map T>
requires(!require_custom_serialization<T>)
simdjson_inline void append(string_builder &b, const T &t) {
append_bounded(b, t);
}
// works for struct
template <class Z>
requires(std::is_class_v<Z> && !concepts::container_but_not_string<Z> &&
!concepts::string_view_keyed_map<Z> &&
!concepts::optional_type<Z> &&
!concepts::smart_pointer<Z> &&
!concepts::appendable_containers<Z> &&
!std::is_same_v<Z, std::string> &&
!std::is_same_v<Z, std::string_view> &&
!std::is_same_v<Z, const char*> &&
!std::is_same_v<Z, char> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
// works for container that have begin() and end() iterators
template <class Z>
requires(concepts::container_but_not_string<Z> && !require_custom_serialization<Z>)
simdjson_inline void append(string_builder &b, const Z &z) {
append_bounded(b, z);
}
template <class Z>
requires (require_custom_serialization<Z>)
void append(string_builder &b, const Z &z) {
b.append(z);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
if constexpr (sizeof(size_t) >= 8 && bound_detail::is_bounded<Z>()) {
// Write straight into s, sized by the bound: no intermediate buffer, no copy.
// Prior related work: jsonifier's serializeJson resizes once through
// resize_and_overwrite (serializer.hpp).
(void)initial_capacity;
const size_t bound = bound_detail::size_bound(z) + unchecked_slack;
auto write = [&z](char *p) noexcept {
unchecked_writer w(p, 0);
atom(w, z);
return w.pos;
};
#if defined(__cpp_lib_string_resize_and_overwrite) && __cpp_lib_string_resize_and_overwrite >= 202110L
s.resize_and_overwrite(bound, [&write](char *p, size_t) noexcept { return write(p); });
#else
s.resize(bound);
s.resize(write(s.data()));
#endif
return SUCCESS;
} else {
string_builder b(initial_capacity);
append(b, z);
std::string_view view;
if(auto e = b.view().get(view); e) { return e; }
s.assign(view);
return SUCCESS;
}
}
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json_string(const Z &z, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
std::string s;
if(auto e = to_json(z, s, initial_capacity); e) { return e; }
return s;
}
template <class Z>
string_builder& operator<<(string_builder& b, const Z& z) {
append(b, z);
return b;
}
// extract_from: Serialize only specific fields from a struct to JSON
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
void extract_from(string_builder &b, const T &obj) {
writer w(b);
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '{';
bool first = true;
// Iterate through all members of T using reflection
static constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));
template for (constexpr auto mem : members) {
if constexpr (std::meta::is_public(mem)) {
static constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only serialize this field if it's in our list of requested fields
if constexpr (((FieldNames.view() == key) || ...)) {
static constexpr auto first_key = std::define_static_string(
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
static constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(mem)) + ":");
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (!w.ensure(rest_key_len)) { w.sync(); return; }
if (first) {
std::memcpy(w.ptr + w.pos, first_key, first_key_len);
w.pos += first_key_len;
} else {
std::memcpy(w.ptr + w.pos, rest_key, rest_key_len);
w.pos += rest_key_len;
}
first = false;
atom(w, obj.[:mem:]);
}
}
};
if (!w.ensure(1)) { w.sync(); return; }
w.ptr[w.pos++] = '}';
w.sync();
}
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
string_builder b(initial_capacity);
extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
SIMDJSON_POP_DISABLE_WARNINGS
} // namespace builder
} // namespace rvv_vls
// Alias the function template to 'to' in the global namespace
template <class Z>
simdjson_warn_unused simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return rvv_vls::builder::to_json_string(z, initial_capacity);
}
template <class Z>
simdjson_warn_unused error_code to_json(const Z &z, std::string &s, size_t initial_capacity = rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return rvv_vls::builder::to_json(z, s, initial_capacity);
}
// Global namespace function for extract_from
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_from(const T &obj, size_t initial_capacity = rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
rvv_vls::builder::string_builder b(initial_capacity);
rvv_vls::builder::extract_from<FieldNames...>(b, obj);
std::string_view s;
if(auto e = b.view().get(s); e) { return e; }
return std::string(s);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif
/* end file simdjson/generic/builder/json_builder.h for rvv_vls */
/* including simdjson/generic/builder/fractured_json_builder.h for rvv_vls: #include "simdjson/generic/builder/fractured_json_builder.h" */
/* begin file simdjson/generic/builder/fractured_json_builder.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
#define SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/fractured_json.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_STATIC_REFLECTION
namespace simdjson {
namespace rvv_vls {
namespace builder {
/**
* Serialize an object to a FracturedJson-formatted string.
*
* FracturedJson produces human-readable yet compact JSON output by intelligently
* choosing between different layout strategies (inline, compact multiline, table,
* expanded) based on content complexity, length, and structure similarity.
*
* This function combines the builder's serialization with FracturedJson formatting:
* 1. Serializes the object to minified JSON using reflection
* 2. Parses and reformats using FracturedJson
*
* Example:
* struct User { int id; std::string name; bool active; };
* User user{1, "Alice", true};
* auto result = to_fractured_json_string(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\", \"active\": true }"
*
* @param obj The object to serialize (must be a reflectable type)
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string, or an error
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Serialize to minified JSON
std::string formatted;
auto error = to_json_string(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
/**
* Extract specific fields from an object and format with FracturedJson.
*
* Example:
* struct User { int id; std::string name; std::string email; bool active; };
* User user{1, "Alice", "alice@example.com", true};
* auto result = extract_fractured_json<"id", "name">(user);
* // result.value() == "{ \"id\": 1, \"name\": \"Alice\" }"
*
* @param obj The object to serialize
* @param opts FracturedJson formatting options
* @param initial_capacity Initial buffer capacity for serialization
* @return The formatted JSON string containing only the specified fields
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = string_builder::DEFAULT_INITIAL_CAPACITY) {
// Step 1: Extract fields to minified JSON
std::string formatted;
auto error = extract_from<FieldNames...>(obj, initial_capacity).get(formatted);
if (error) {
return error;
}
// Step 2: Reformat with FracturedJson
return fractured_json_string(formatted, opts);
}
} // namespace builder
} // namespace rvv_vls
// Global namespace convenience functions
/**
* Serialize an object to a FracturedJson-formatted string.
* Global namespace version for convenience.
*/
template <class T>
simdjson_warn_unused simdjson_result<std::string> to_fractured_json_string(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return rvv_vls::builder::to_fractured_json_string(obj, opts, initial_capacity);
}
/**
* Extract specific fields from an object and format with FracturedJson.
* Global namespace version for convenience.
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_result<std::string> extract_fractured_json(
const T& obj,
const fractured_json_options& opts = {},
size_t initial_capacity = rvv_vls::builder::string_builder::DEFAULT_INITIAL_CAPACITY) {
return rvv_vls::builder::extract_fractured_json<FieldNames...>(obj, opts, initial_capacity);
}
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_FRACTURED_JSON_BUILDER_H
/* end file simdjson/generic/builder/fractured_json_builder.h for rvv_vls */
// JSON builder inline definitions
/* including simdjson/generic/builder/json_string_builder-inl.h for rvv_vls: #include "simdjson/generic/builder/json_string_builder-inl.h" */
/* begin file simdjson/generic/builder/json_string_builder-inl.h for rvv_vls */
#include <array>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <type_traits>
#ifndef SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_STRING_BUILDER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_string_builder.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/*
* Empirically, we have found that an inlined optimization is important for
* performance. The following macros are not ideal. We should find a better
* way to inline the code.
*/
#if defined(__SSE2__) || defined(__x86_64__) || defined(__x86_64) || \
(defined(_M_AMD64) || defined(_M_X64) || \
(defined(_M_IX86_FP) && _M_IX86_FP == 2))
#ifndef SIMDJSON_EXPERIMENTAL_HAS_SSE2
#define SIMDJSON_EXPERIMENTAL_HAS_SSE2 1
#endif
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_EXPERIMENTAL_HAS_NEON 1
#endif
#endif
#if defined(__loongarch_sx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LSX
#define SIMDJSON_EXPERIMENTAL_HAS_LSX 1
#endif
#endif
#if defined(__loongarch_asx)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_LASX
#define SIMDJSON_EXPERIMENTAL_HAS_LASX 1
#endif
#endif
#if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 11000 && \
defined(__riscv_vector)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_RVV
#define SIMDJSON_EXPERIMENTAL_HAS_RVV 1
#endif
#endif
#if (defined(__PPC64__) || defined(_M_PPC64)) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
#ifndef SIMDJSON_EXPERIMENTAL_HAS_PPC64
#define SIMDJSON_EXPERIMENTAL_HAS_PPC64 1
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
#include <emmintrin.h>
#if defined(__AVX2__)
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LSX
#include <lsxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_LASX
#include <lasxintrin.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_RVV
#include <riscv_vector.h>
#endif
#if SIMDJSON_EXPERIMENTAL_HAS_PPC64
#include <altivec.h>
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
#endif
namespace simdjson {
namespace rvv_vls {
namespace builder {
static SIMDJSON_CONSTEXPR_LAMBDA std::array<uint8_t, 256>
json_quotable_character = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
A possible SWAR implementation of has_json_escapable_byte. It is not used
because it is slower than the current implementation. It is kept here for
reference (to show that we tried it).
inline bool has_json_escapable_byte(uint64_t x) {
uint64_t is_ascii = 0x8080808080808080ULL & ~x;
uint64_t xor2 = x ^ 0x0202020202020202ULL;
uint64_t lt32_or_eq34 = xor2 - 0x2121212121212121ULL;
uint64_t sub92 = x ^ 0x5C5C5C5C5C5C5C5CULL;
uint64_t eq92 = (sub92 - 0x0101010101010101ULL);
return ((lt32_or_eq34 | eq92) & is_ascii) != 0;
}
**/
// Scalar fallback for finding next quotable character
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character_scalar(const std::string_view view,
size_t location) noexcept {
for (auto pos = view.begin() + location; pos != view.end(); ++pos) {
if (json_quotable_character[static_cast<uint8_t>(*pos)]) {
return pos - view.begin();
}
}
return size_t(view.size());
}
// SIMD-accelerated position finding that directly locates the first quotable
// character, combining detection and position extraction in a single pass to
// minimize redundant work.
#if SIMDJSON_EXPERIMENTAL_HAS_NEON
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
uint8x16_t v34 = vdupq_n_u8(34); // '"'
uint8x16_t v92 = vdupq_n_u8(92); // '\\'
uint8x16_t v32 = vdupq_n_u8(32); // control char threshold
while (remaining >= 16) {
uint8x16_t word = vld1q_u8(ptr);
// Check for quotable characters: '"', '\\', or control chars (< 32)
uint8x16_t needs_escape = vceqq_u8(word, v34);
needs_escape = vorrq_u8(needs_escape, vceqq_u8(word, v92));
needs_escape = vorrq_u8(needs_escape, vcltq_u8(word, v32));
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(needs_escape), 4);
const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
if(mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
auto trailing_zero = trailing_zeroes(mask);
return offset + (trailing_zero >> 2);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_SSE2
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants
__m128i v34 = _mm_set1_epi8(34); // '"'
__m128i v92 = _mm_set1_epi8(92); // '\\'
__m128i v31 = _mm_set1_epi8(31); // for control char detection
while (remaining >= 16) {
__m128i word = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
// Check for quotable characters
__m128i needs_escape = _mm_cmpeq_epi8(word, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(word, v92));
needs_escape = _mm_or_si128(
needs_escape,
_mm_cmpeq_epi8(_mm_subs_epu8(word, v31), _mm_setzero_si128()));
int mask = _mm_movemask_epi8(needs_escape);
if (mask != 0) {
// Found quotable character - use trailing zero count to find position
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + trailing_zeroes(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LASX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__m256i v34 = __lasx_xvreplgr2vr_b(34); // '"'
__m256i v92 = __lasx_xvreplgr2vr_b(92); // '\\'
__m256i v32 = __lasx_xvreplgr2vr_b(32); // control char threshold
while (remaining >= 32) {
__m256i word = __lasx_xvld(ptr, 0);
// Check for quotable characters: '"', '\\', or control chars (< 32)
__m256i needs_escape = __lasx_xvseq_b(word, v34);
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvseq_b(word, v92));
needs_escape = __lasx_xvor_v(needs_escape, __lasx_xvslt_bu(word, v32));
if (!__lasx_xbz_v(needs_escape)) {
// Found a quotable character - locate it via the four 64-bit lanes
uint64_t lane0 = __lasx_xvpickve2gr_du(needs_escape, 0);
uint64_t lane1 = __lasx_xvpickve2gr_du(needs_escape, 1);
uint64_t lane2 = __lasx_xvpickve2gr_du(needs_escape, 2);
uint64_t lane3 = __lasx_xvpickve2gr_du(needs_escape, 3);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if (lane0 != 0) {
return offset + trailing_zeroes(lane0) / 8;
} else if (lane1 != 0) {
return offset + 8 + trailing_zeroes(lane1) / 8;
} else if (lane2 != 0) {
return offset + 16 + trailing_zeroes(lane2) / 8;
} else {
return offset + 24 + trailing_zeroes(lane3) / 8;
}
}
ptr += 32;
remaining -= 32;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_LSX
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
//SIMD constants for characters requiring escape
__m128i v34 = __lsx_vreplgr2vr_b(34); // '"'
__m128i v92 = __lsx_vreplgr2vr_b(92); // '\\'
__m128i v32 = __lsx_vreplgr2vr_b(32); // control char threshold
while (remaining >= 16){
__m128i word = __lsx_vld(ptr, 0);
//Check for the quotable characters: '"', '\\', or control char (<32)
__m128i needs_escape = __lsx_vseq_b(word, v34);
needs_escape = __lsx_vor_v(needs_escape, __lsx_vseq_b(word, v92));
needs_escape = __lsx_vor_v(needs_escape, __lsx_vslt_bu(word, v32));
if (!__lsx_bz_v(needs_escape)){
//Found quotable character - extract exact byte position
uint64_t lo = __lsx_vpickve2gr_du(needs_escape,0);
uint64_t hi = __lsx_vpickve2gr_du(needs_escape,1);
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
if ( lo != 0) {
return offset + trailing_zeroes(lo) / 8;
} else {
return offset + 8 + trailing_zeroes(hi) / 8;
}
}
ptr += 16;
remaining -= 16;
}
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#elif SIMDJSON_EXPERIMENTAL_HAS_RVV
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
while (remaining > 0) {
size_t vl = __riscv_vsetvl_e8m1(remaining);
vuint8m1_t word = __riscv_vle8_v_u8m1(ptr, vl);
// Check for quotable characters: '"', '\\', or control chars (< 32)
vbool8_t needs_escape = __riscv_vmseq(word, (uint8_t)34, vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmseq(word, (uint8_t)92, vl), vl);
needs_escape = __riscv_vmor(needs_escape,
__riscv_vmsltu(word, (uint8_t)32, vl), vl);
long first = __riscv_vfirst(needs_escape, vl);
if (first >= 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + first;
}
ptr += vl;
remaining -= vl;
}
return len;
}
#elif SIMDJSON_EXPERIMENTAL_HAS_PPC64
simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
const size_t len = view.size();
const uint8_t *ptr =
reinterpret_cast<const uint8_t *>(view.data()) + location;
size_t remaining = len - location;
// SIMD constants for characters requiring escape
__vector unsigned char v34 = vec_splats((unsigned char)34); // '"'
__vector unsigned char v92 = vec_splats((unsigned char)92); // '\\'
__vector unsigned char v32 = vec_splats((unsigned char)32); // control char threshold
// Bitmask for vec_vbpermq to extract one bit per byte
const __vector unsigned char perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50,
0x48, 0x40, 0x38, 0x30, 0x28, 0x20,
0x18, 0x10, 0x08, 0x00};
while (remaining >= 16) {
__vector unsigned char word =
vec_vsx_ld(0, reinterpret_cast<const unsigned char *>(ptr));
// Check for quotable characters: '"', '\\', or control chars (< 32)
__vector unsigned char needs_escape =
(__vector unsigned char)vec_cmpeq(word, v34);
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmpeq(word, v92));
needs_escape = vec_or(needs_escape,
(__vector unsigned char)vec_cmplt(word, v32));
__vector unsigned long long result =
(__vector unsigned long long)vec_vbpermq(needs_escape, perm_mask);
#ifdef __LITTLE_ENDIAN__
unsigned int mask = static_cast<unsigned int>(result[1]);
#else
unsigned int mask = static_cast<unsigned int>(result[0]);
#endif
if (mask != 0) {
size_t offset = ptr - reinterpret_cast<const uint8_t *>(view.data());
return offset + __builtin_ctz(mask);
}
ptr += 16;
remaining -= 16;
}
// Scalar fallback for remaining bytes
size_t current = len - remaining;
return find_next_json_quotable_character_scalar(view, current);
}
#else
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline size_t
find_next_json_quotable_character(const std::string_view view,
size_t location) noexcept {
return find_next_json_quotable_character_scalar(view, location);
}
#endif
SIMDJSON_CONSTEXPR_LAMBDA static std::string_view control_chars[] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006",
"\\u0007", "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r",
"\\u000e", "\\u000f", "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014",
"\\u0015", "\\u0016", "\\u0017", "\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f"};
// All Unicode characters may be placed within the quotation marks, except for
// the characters that MUST be escaped: quotation mark, reverse solidus, and the
// control characters (U+0000 through U+001F). There are two-character sequence
// escape representations of some popular characters:
// \", \\, \b, \f, \n, \r, \t.
SIMDJSON_CONSTEXPR_LAMBDA simdjson_inline void escape_json_char(char c, char *&out) {
if (c == '"') {
memcpy(out, "\\\"", 2);
out += 2;
} else if (c == '\\') {
memcpy(out, "\\\\", 2);
out += 2;
} else {
std::string_view v = control_chars[uint8_t(c)];
memcpy(out, v.data(), v.size());
out += v.size();
}
}
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 || SIMDJSON_EXPERIMENTAL_HAS_NEON
#define SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE 1
#endif
#if SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2
using escape_vector = __m128i;
// Mask bits that each input byte contributes to escape_bitmask().
static constexpr unsigned escape_mask_bits = 1;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
_mm_storeu_si128(reinterpret_cast<__m128i *>(out), v);
}
// Builds a vector whose bytes 0..7 come from a and bytes 8..15 from b.
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
return _mm_unpacklo_epi64(
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(a)),
_mm_loadl_epi64(reinterpret_cast<const __m128i *>(b)));
}
// Builds a vector whose bytes 0..3 come from a and bytes 4..7 from b. The
// upper half is unspecified; callers only look at the low eight bits.
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
int32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return _mm_unpacklo_epi32(_mm_cvtsi32_si128(a32), _mm_cvtsi32_si128(b32));
}
// Sets every bit of byte k when byte k of v is a quotable character ('"',
// '\\' or a control character).
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
const __m128i v34 = _mm_set1_epi8(34); // '"'
const __m128i v92 = _mm_set1_epi8(92); // '\\'
const __m128i v31 = _mm_set1_epi8(31); // for control char detection
__m128i needs_escape = _mm_cmpeq_epi8(v, v34);
needs_escape = _mm_or_si128(needs_escape, _mm_cmpeq_epi8(v, v92));
return _mm_or_si128(
needs_escape, _mm_cmpeq_epi8(_mm_subs_epu8(v, v31), _mm_setzero_si128()));
}
// True when any byte needs escaping. Kept separate from escape_bitmask
// because some instruction sets can answer it without leaving the vector
// register file; on SSE2 the compiler folds the two together.
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return _mm_movemask_epi8(flags) != 0;
}
// A 16-bit mask with bit k set when byte k needed escaping.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
return uint64_t(uint32_t(_mm_movemask_epi8(flags)));
}
#else // SIMDJSON_EXPERIMENTAL_HAS_NEON
using escape_vector = uint8x16_t;
static constexpr unsigned escape_mask_bits = 4;
simdjson_inline escape_vector escape_load16(const uint8_t *p) noexcept {
return vld1q_u8(p);
}
simdjson_inline void escape_store16(char *out, escape_vector v) noexcept {
vst1q_u8(reinterpret_cast<uint8_t *>(out), v);
}
simdjson_inline escape_vector escape_load8x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint64_t a64, b64;
memcpy(&a64, a, 8);
memcpy(&b64, b, 8);
return vreinterpretq_u8_u64(vsetq_lane_u64(b64, vdupq_n_u64(a64), 1));
}
simdjson_inline escape_vector escape_load4x2(const uint8_t *a,
const uint8_t *b) noexcept {
uint32_t a32, b32;
memcpy(&a32, a, 4);
memcpy(&b32, b, 4);
return vreinterpretq_u8_u32(vsetq_lane_u32(b32, vdupq_n_u32(a32), 1));
}
simdjson_inline escape_vector escape_flags(escape_vector v) noexcept {
uint8x16_t needs_escape = vceqq_u8(v, vdupq_n_u8(34)); // '"'
needs_escape = vorrq_u8(needs_escape, vceqq_u8(v, vdupq_n_u8(92))); // '\\'
return vorrq_u8(needs_escape, vcltq_u8(v, vdupq_n_u8(32)));
}
simdjson_inline bool escape_any(escape_vector flags) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(flags)) != 0;
}
// Four bits per byte rather than one: escape_block and the tail paths scale
// their shifts by escape_mask_bits to match.
simdjson_inline uint64_t escape_bitmask(escape_vector flags) noexcept {
uint8x8_t narrowed = vshrn_n_u16(vreinterpretq_u16_u8(flags), 4);
return vget_lane_u64(vreinterpret_u64_u8(narrowed), 0);
}
#endif // instruction set selection
// The escape bitmask of a 16-byte block.
simdjson_inline uint64_t escape_mask(escape_vector v) noexcept {
return escape_bitmask(escape_flags(v));
}
// Copies n bytes with n < 16, using overlapping loads and stores. It never
// reads more than n bytes from src, nor writes more than n bytes to dst.
simdjson_inline void copy_lt16(char *dst, const uint8_t *src,
size_t n) noexcept {
if (n >= 8) {
memcpy(dst, src, 8);
memcpy(dst + n - 8, src + n - 8, 8);
} else if (n >= 4) {
memcpy(dst, src, 4);
memcpy(dst + n - 4, src + n - 4, 4);
} else if (n > 0) {
dst[0] = char(src[0]);
dst[n >> 1] = char(src[n >> 1]);
dst[n - 1] = char(src[n - 1]);
}
}
// Escapes the bytes of src in the range [i, blockend), given that m is the
// (non-zero) escape mask for that range: the escape_mask_bits-wide lane of m
// at byte k is non-zero when src[i + k] requires escaping. Returns the updated
// output pointer.
simdjson_never_inline char *escape_block(const uint8_t *src, char *out,
size_t i, size_t blockend,
uint64_t m) noexcept {
constexpr uint64_t lane = (uint64_t(1) << escape_mask_bits) - 1;
size_t pos = i; // first byte not yet copied
while (m) {
const size_t tz = trailing_zeroes(m);
const size_t next = i + tz / escape_mask_bits;
// Copy the run of safe bytes that precedes this escape.
copy_lt16(out, src + pos, next - pos);
out += next - pos;
escape_json_char(char(src[next]), out);
pos = next + 1;
m &= ~(lane << tz);
}
// Copy whatever follows the last escape.
copy_lt16(out, src + pos, blockend - pos);
return out + (blockend - pos);
}
// Writes the escaped version of input to out, returning the number of bytes
// written.
simdjson_really_inline size_t write_string_escaped(const std::string_view input, char *out) {
const size_t len = input.size();
const uint8_t *src = reinterpret_cast<const uint8_t *>(input.data());
const char *const initout = out;
size_t i = 0;
#if SIMDJSON_EXPERIMENTAL_HAS_SSE2 && defined(__AVX2__)
while (i + 32 <= len) {
const __m256i word = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
const __m256i flags = _mm256_or_si256(
_mm256_or_si256(_mm256_cmpeq_epi8(word, _mm256_set1_epi8(34)), // '"'
_mm256_cmpeq_epi8(word, _mm256_set1_epi8(92))), // '\\'
_mm256_cmpeq_epi8(_mm256_subs_epu8(word, _mm256_set1_epi8(31)),
_mm256_setzero_si256())); // control
const uint32_t mask = uint32_t(_mm256_movemask_epi8(flags));
if (simdjson_likely(mask == 0)) {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(out), word);
out += 32;
} else {
for (size_t half = 0; half < 32; half += 16) {
const uint64_t m = (mask >> half) & 0xFFFF;
if (m == 0) {
escape_store16(out, escape_load16(src + i + half));
out += 16;
} else {
out = escape_block(src, out, i + half, i + half + 16, m);
}
}
}
i += 32;
}
#endif
while (i + 16 <= len) {
escape_vector word = escape_load16(src + i);
escape_vector flags = escape_flags(word);
if (simdjson_likely(!escape_any(flags))) {
escape_store16(out, word);
out += 16;
} else {
out = escape_block(src, out, i, i + 16, escape_bitmask(flags));
}
i += 16;
}
if (i < len) {
const size_t rem = len - i;
uint64_t m;
if (len >= 16) {
// The last 16 bytes of the input are in bounds. Bit k of that block's
// mask belongs to input position len - 16 + k, so shift it down to align
// bit 0 with position i.
m = escape_mask(escape_load16(src + len - 16)) >>
(escape_mask_bits * (16 - rem));
} else if (len >= 8) {
// Here i == 0 and rem == len. Two overlapping 8-byte loads cover
// [0, 8) and [len - 8, len), which is the whole input since len < 16.
uint64_t mm = escape_mask(escape_load8x2(src, src + len - 8));
constexpr uint64_t low8 = (uint64_t(1) << (escape_mask_bits * 8)) - 1;
m = (mm & low8) |
((mm >> (escape_mask_bits * 8)) << (escape_mask_bits * (len - 8)));
} else if (len >= 4) {
// Same idea with two overlapping 4-byte loads.
uint64_t mm = escape_mask(escape_load4x2(src, src + len - 4));
constexpr uint64_t low4 = (uint64_t(1) << (escape_mask_bits * 4)) - 1;
m = (mm & low4) | (((mm >> (escape_mask_bits * 4)) & low4)
<< (escape_mask_bits * (len - 4)));
} else {
// Fewer than 4 bytes: at most three table lookups, no need for SIMD.
for (size_t k = 0; k < len; k++) {
uint8_t c = src[k];
if (json_quotable_character[c]) {
escape_json_char(char(c), out);
} else {
*out++ = char(c);
}
}
return size_t(out - initout);
}
if (m == 0) {
copy_lt16(out, src + i, rem);
out += rem;
} else {
out = escape_block(src, out, i, len, m);
}
}
return size_t(out - initout);
}
#else // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
// Writes the escaped version of input to out, returning the number of bytes
// written. Uses SIMD position finding to locate quotable characters efficiently.
inline size_t write_string_escaped(const std::string_view input, char *out) {
size_t mysize = input.size();
// Use SIMD position finder directly - it returns mysize if no escape needed
size_t location = find_next_json_quotable_character(input, 0);
if (location == mysize) {
// Fast path: no escaping needed
memcpy(out, input.data(), input.size());
return input.size();
}
const char *const initout = out;
memcpy(out, input.data(), location);
out += location;
escape_json_char(input[location], out);
location += 1;
while (location < mysize) {
size_t newlocation = find_next_json_quotable_character(input, location);
memcpy(out, input.data() + location, newlocation - location);
out += newlocation - location;
location = newlocation;
if (location == mysize) {
break;
}
escape_json_char(input[location], out);
location += 1;
}
return size_t(out - initout);
}
#endif // SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
#undef SIMDJSON_BUILDER_HAS_BLOCK_ESCAPE
simdjson_inline string_builder::string_builder(size_t initial_capacity)
: buffer(new(std::nothrow) char[initial_capacity]), position(0),
capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) {
return true;
}
// check for overflow, most of the time there is no overflow
if (simdjson_unlikely(position + upcoming_bytes < position)) {
return false;
}
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) {
return;
}
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if (!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if (capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if (!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// Integer to decimal: James Edward Anhalt III's algorithm
static const char jeaiii_dd[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static const char jeaiii_fd[201] =
"0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0"
"10111213141516171819202122232425262728293031323334353637383940414243444546474849"
"50515253545556575859606162636465666768697071727374757677787980818283848586878889"
"90919293949596979899";
simdjson_really_inline void jeaiii_write_dd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_dd[2 * k], 2);
}
simdjson_really_inline void jeaiii_write_fd(char *p, uint64_t k) noexcept {
std::memcpy(p, &jeaiii_fd[2 * k], 2);
}
// Caller guarantees n < 10^8. Writes 1 to 8 digits.
simdjson_really_inline char *jeaiii_lt1e8(char *b, uint32_t n) noexcept {
constexpr uint64_t mask24 = (uint64_t(1) << 24) - 1;
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
if (n < 100) {
jeaiii_write_fd(b, n);
return n < 10 ? b + 1 : b + 2;
}
if (n < 1000000) {
if (n < 10000) {
const uint32_t f0 = uint32_t(10 * (1 << 24) / 1e3 + 1) * n;
jeaiii_write_fd(b, f0 >> 24);
b -= n < 1000;
const uint32_t f2 = uint32_t(f0 & mask24) * 100;
jeaiii_write_dd(b + 2, f2 >> 24);
return b + 4;
}
const uint64_t f0 = uint64_t(10 * (1ull << 32) / 1e5 + 1) * n;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 100000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
return b + 6;
}
const uint64_t f0 = uint64_t(10 * (1ull << 48) / 1e7 + 1) * n >> 16;
jeaiii_write_fd(b, f0 >> 32);
b -= n < 10000000;
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees z < 10^8. Always writes exactly 8 digits.
simdjson_really_inline char *jeaiii_8_digits(char *b, uint32_t z) noexcept {
constexpr uint64_t mask32 = (uint64_t(1) << 32) - 1;
const uint64_t f0 = (uint64_t((1ull << 48) / 1e6 + 1) * z >> 16) + 1;
jeaiii_write_dd(b, f0 >> 32);
const uint64_t f2 = (f0 & mask32) * 100;
jeaiii_write_dd(b + 2, f2 >> 32);
const uint64_t f4 = (f2 & mask32) * 100;
jeaiii_write_dd(b + 4, f4 >> 32);
const uint64_t f6 = (f4 & mask32) * 100;
jeaiii_write_dd(b + 6, f6 >> 32);
return b + 8;
}
// Caller guarantees 10^8 <= n < 2^32. Writes 9 or 10 digits.
simdjson_really_inline char *jeaiii_9_or_10(char *b, uint64_t n) noexcept {
constexpr uint64_t mask57 = (uint64_t(1) << 57) - 1;
const uint64_t f0 = uint64_t(10 * (1ull << 57) / 1e9 + 1) * n;
jeaiii_write_fd(b, f0 >> 57);
b -= n < 1000000000;
const uint64_t f2 = (f0 & mask57) * 100;
jeaiii_write_dd(b + 2, f2 >> 57);
const uint64_t f4 = (f2 & mask57) * 100;
jeaiii_write_dd(b + 4, f4 >> 57);
const uint64_t f6 = (f4 & mask57) * 100;
jeaiii_write_dd(b + 6, f6 >> 57);
const uint64_t f8 = (f6 & mask57) * 100;
jeaiii_write_dd(b + 8, f8 >> 57);
return b + 10;
}
simdjson_really_inline char *write_uint_jeaiii(char *b, uint64_t n) noexcept {
if (n < 100000000) {
return jeaiii_lt1e8(b, uint32_t(n));
}
if (n < (uint64_t(1) << 32)) {
return jeaiii_9_or_10(b, n);
}
// At least 10 digits: the low 8 digits, and 2 to 12 digits above them.
const uint32_t z = uint32_t(n % 100000000);
uint64_t u = n / 100000000;
if (u < 100000000) {
// u has 2 to 8 digits (if u < 10, n would be below 2^32).
b = jeaiii_lt1e8(b, uint32_t(u));
} else if (u < (uint64_t(1) << 32)) {
b = jeaiii_9_or_10(b, u);
} else {
// u has 11 or 12 digits: split off 8 more.
const uint32_t y = uint32_t(u % 100000000);
u /= 100000000;
b = jeaiii_lt1e8(b, uint32_t(u)); // 3 or 4 digits
b = jeaiii_8_digits(b, y);
}
return jeaiii_8_digits(b, z);
}
// Writes v at p, which must have to_chars_buffer_size bytes available, and
// returns the end of what was written.
simdjson_inline char *write_double(char *p, double v) noexcept {
#if SIMDJSON_ENABLE_NAN_INF
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
std::memcpy(p, "NaN", 3);
return p + 3;
}
if (v < 0) {
*p++ = '-';
}
std::memcpy(p, "Infinity", 8);
return p + 8;
}
#endif
return simdjson::internal::to_chars(p, nullptr, v);
}
} // namespace internal
template <typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value ||
std::is_integral<number_type>::value ||
std::is_floating_point<number_type>::value,
"Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
SIMDJSON_IF_CONSTEXPR(std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if (capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if (capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
char* end = internal::write_uint_jeaiii(
buffer.get() + position,
static_cast<uint64_t>(static_cast<unsigned_type>(v)));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_integral<number_type>::value) {
// 19 digits (max abs value of int64_t) + optional minus sign.
constexpr size_t max_number_size = 20;
if (capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
// 0 - pv (rather than -pv) avoids an MSVC unary-minus warning.
unsigned_type pv = negative
? unsigned_type(0) - static_cast<unsigned_type>(v)
: static_cast<unsigned_type>(v);
// Branchless: always write '-', advance only if negative.
buffer.get()[position] = '-';
position += negative;
char* end = internal::write_uint_jeaiii(
buffer.get() + position, static_cast<uint64_t>(pv));
position = end - buffer.get();
}
}
else SIMDJSON_IF_CONSTEXPR(std::is_floating_point<number_type>::value) {
// Must reserve to_chars_buffer_size (40): only ~24 chars are emitted,
// but to_chars over-writes with fixed-size 16/17-byte copies so the
// compiler can inline mem* (see simdjson::internal::to_chars_buffer_size).
constexpr size_t max_number_size = simdjson::internal::to_chars_buffer_size;
if (capacity_check(max_number_size)) {
#if SIMDJSON_ENABLE_NAN_INF
// Check if the input might be NaN or infinity
if (simdjson_unlikely(!std::isfinite(v))) {
if (std::isnan(v)) {
constexpr char nan_literal[] = "NaN";
constexpr size_t nan_len = sizeof(nan_literal) - 1;
std::memcpy(buffer.get() + position, nan_literal, nan_len);
position += nan_len;
} else {
constexpr char inf_literal[] = "Infinity";
constexpr size_t inf_len = sizeof(inf_literal) - 1;
if (v < 0) {
buffer.get()[position] = '-';
++position;
}
std::memcpy(buffer.get() + position, inf_literal, inf_len);
position += inf_len;
}
return;
}
#endif
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr,
double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void
string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the multiplication below.
if (input.size() > (std::numeric_limits<size_t>::max)() / 6) {
set_valid(false);
return;
}
if (capacity_check(6 * input.size())) {
position += write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
// Guard against size_t overflow in the arithmetic below.
if (input.size() > ((std::numeric_limits<size_t>::max)() - 2) / 6) {
set_valid(false);
return;
}
if (capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(char input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if (capacity_check(2 + 6 * 1)) {
buffer.get()[position++] = '"';
std::string_view cinput(&input, 1);
position += write_string_escaped(cinput, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void
string_builder::escape_and_append_with_quotes(const char *input) noexcept {
std::string_view cinput(input);
escape_and_append_with_quotes(cinput);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <constevalutil::fixed_string key>
simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<constevalutil::quoted_escaped_text<key>>();
#else
escape_and_append_with_quotes(constevalutil::string_constant<key>::value);
#endif
}
#endif
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
// char_traits::length is constexpr; lets the compiler fold the length
// when called with a pointer to a compile-time-constant string.
size_t len = std::char_traits<char>::length(c);
append_raw(c, len);
}
simdjson_inline void
string_builder::append_raw(std::string_view input) noexcept {
if (capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str,
size_t len) noexcept {
if (capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL
template <typename Text>
simdjson_inline void string_builder::append_static_string() noexcept {
constexpr auto& padded = constevalutil::static_string<Text>;
if (capacity_check(padded.size())) {
std::memcpy(buffer.get() + position, padded.data(), padded.size());
position += constevalutil::static_string_size<Text>();
}
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
requires(!require_custom_serialization<T>)
simdjson_inline void string_builder::append(const T &opt) {
if (opt) {
append(*opt);
} else {
append_null();
}
}
template <typename T>
requires(require_custom_serialization<T>)
simdjson_inline void string_builder::append(T &&val) {
serialize(*this, std::forward<T>(val));
}
template <typename T>
requires(std::is_convertible<T, std::string_view>::value ||
std::is_same<T, const char *>::value)
simdjson_inline void string_builder::append(const T &value) {
escape_and_append_with_quotes(value);
}
#endif
#if SIMDJSON_SUPPORTS_RANGES && SIMDJSON_SUPPORTS_CONCEPTS
// Support for range-based appending (std::ranges::view, etc.)
template <std::ranges::range R>
requires(!std::is_convertible<R, std::string_view>::value && !concepts::optional_type<R> && !require_custom_serialization<R>)
simdjson_inline void string_builder::append(const R &range) noexcept {
auto it = std::ranges::begin(range);
auto end = std::ranges::end(range);
if constexpr (concepts::is_pair<std::ranges::range_value_t<R>>) {
start_object();
if (it == end) {
end_object();
return; // Handle empty range
}
// Append first item without leading comma
append_key_value(it->first, it->second);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append_key_value(it->first, it->second);
}
end_object();
} else {
start_array();
if (it == end) {
end_array();
return; // Handle empty range
}
// Append first item without leading comma
append(*it);
++it;
// Append remaining items with preceding commas
for (; it != end; ++it) {
append_comma();
append(*it);
}
end_array();
}
}
#endif
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(operator std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const
noexcept(false) simdjson_lifetime_bound {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view>
string_builder::view() const noexcept {
if (!is_valid) {
return simdjson::OUT_OF_CAPACITY;
}
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if (capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
simdjson_inline void string_builder::start_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '{';
}
}
simdjson_inline void string_builder::end_object() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '}';
}
}
simdjson_inline void string_builder::start_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = '[';
}
}
simdjson_inline void string_builder::end_array() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ']';
}
}
simdjson_inline void string_builder::append_comma() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ',';
}
}
simdjson_inline void string_builder::append_colon() noexcept {
if (capacity_check(1)) {
buffer.get()[position++] = ':';
}
}
template <typename key_type, typename value_type>
simdjson_inline void
string_builder::append_key_value(key_type key, value_type value) noexcept {
static_assert(std::is_same<key_type, const char *>::value ||
std::is_convertible<key_type, std::string_view>::value,
"Unsupported key type");
escape_and_append_with_quotes(key);
append_colon();
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
#if SIMDJSON_CONSTEVAL
template <constevalutil::fixed_string key>
struct quoted_key_text {
static consteval std::string text() {
return constevalutil::consteval_to_quoted_escaped(key.view()) + ":";
}
};
#endif
template <constevalutil::fixed_string key, typename value_type>
simdjson_inline void
string_builder::append_key_value(value_type value) noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<quoted_key_text<key>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
#endif
SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, std::nullptr_t>::value) {
append_null();
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, char>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(
std::is_convertible<value_type, std::string_view>::value) {
escape_and_append_with_quotes(value);
}
else SIMDJSON_IF_CONSTEXPR(std::is_same<value_type, const char *>::value) {
escape_and_append_with_quotes(value);
}
else {
append(value);
}
}
#if SIMDJSON_CONSTEVAL
template <typename T>
concept compile_time_formattable =
constevalutil::is_fixed_string<std::remove_cvref_t<T>> ||
std::same_as<std::remove_cvref_t<T>, bool> ||
std::same_as<std::remove_cvref_t<T>, std::nullptr_t>;
template <constevalutil::fixed_string key, auto value>
requires compile_time_formattable<decltype(value)>
struct key_value_text {
static consteval std::string text() {
using value_type = std::remove_cvref_t<decltype(value)>;
std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':';
SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string<value_type>)
out += constevalutil::consteval_to_quoted_escaped(value.view());
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, bool>)
out += value ? "true" : "false";
else SIMDJSON_IF_CONSTEXPR(std::same_as<value_type, std::nullptr_t>)
out += "null";
return out;
}
};
#endif
template <constevalutil::fixed_string key, constevalutil::fixed_string value>
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
append_static_string<key_value_text<key, value>>();
#else
escape_and_append_with_quotes<key>();
append_colon();
escape_and_append_with_quotes<value>();
#endif
}
template <constevalutil::fixed_string key, auto value>
requires (!constevalutil::is_fixed_string<std::remove_cvref_t<decltype(value)>>)
simdjson_inline void
string_builder::append_key_value() noexcept {
#if SIMDJSON_CONSTEVAL
SIMDJSON_IF_CONSTEXPR(compile_time_formattable<decltype(value)>) {
append_static_string<key_value_text<key, value>>();
} else
#endif
{
append_key_value<key>(value);
}
}
#endif
} // namespace builder
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_STRING_BUILDER_INL_H
/* end file simdjson/generic/builder/json_string_builder-inl.h for rvv_vls */
/* end file simdjson/generic/builder/amalgamated.h for rvv_vls */
/* including simdjson/rvv-vls/end.h: #include "simdjson/rvv-vls/end.h" */
/* begin file simdjson/rvv-vls/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/* undefining SIMDJSON_IMPLEMENTATION from "rvv_vls" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/rvv-vls/end.h */
#endif // SIMDJSON_RVV_VLS_BUILDER_H
/* end file simdjson/rvv-vls/builder.h */
#else
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
#endif
/* undefining SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
/**
* @copydoc simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::builder
*/
namespace builder = SIMDJSON_BUILTIN_IMPLEMENTATION::builder;
} // namespace simdjson
#endif // SIMDJSON_BUILTIN_BUILDER_H
/* end file simdjson/builtin/builder.h */
namespace simdjson {
/**
* @copydoc simdjson::builtin::builder
*/
namespace builder = builtin::builder;
} // namespace simdjson
#endif // SIMDJSON_BUILDER_H
/* end file simdjson/builder.h */
/* including simdjson/ondemand.h: #include "simdjson/ondemand.h" */
/* begin file simdjson/ondemand.h */
#ifndef SIMDJSON_ONDEMAND_H
#define SIMDJSON_ONDEMAND_H
/* including simdjson/builtin/ondemand.h: #include "simdjson/builtin/ondemand.h" */
/* begin file simdjson/builtin/ondemand.h */
#ifndef SIMDJSON_BUILTIN_ONDEMAND_H
#define SIMDJSON_BUILTIN_ONDEMAND_H
/* skipped duplicate #include "simdjson/builtin.h" */
/* skipped duplicate #include "simdjson/builtin/base.h" */
/* including simdjson/generic/ondemand/dependencies.h: #include "simdjson/generic/ondemand/dependencies.h" */
/* begin file simdjson/generic/ondemand/dependencies.h */
#ifdef SIMDJSON_CONDITIONAL_INCLUDE
#error simdjson/generic/ondemand/dependencies.h must be included before defining SIMDJSON_CONDITIONAL_INCLUDE!
#endif
#ifndef SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H
#define SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H
// Internal headers needed for ondemand generics.
// All includes not under simdjson/generic/ondemand must be here!
// Otherwise, amalgamation will fail.
/* skipped duplicate #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* skipped duplicate #include "simdjson/implementation.h" */
/* skipped duplicate #include "simdjson/base.h" */
/* skipped duplicate #include "simdjson/common_defs.h" */
/* skipped duplicate #include "simdjson/constevalutil.h" */
/* skipped duplicate #include "simdjson/padded_string.h" */
/* skipped duplicate #include "simdjson/padded_string_view.h" */
/* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */
/* skipped duplicate #include "simdjson/jsonpathutil.h" */
/* skipped duplicate #include "simdjson/annotations.h" */
#endif // SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H
/* end file simdjson/generic/ondemand/dependencies.h */
/* defining SIMDJSON_CONDITIONAL_INCLUDE */
#define SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_BUILTIN_IMPLEMENTATION_IS(arm64)
/* including simdjson/arm64/ondemand.h: #include "simdjson/arm64/ondemand.h" */
/* begin file simdjson/arm64/ondemand.h */
#ifndef SIMDJSON_ARM64_ONDEMAND_H
#define SIMDJSON_ARM64_ONDEMAND_H
/* including simdjson/arm64/begin.h: #include "simdjson/arm64/begin.h" */
/* begin file simdjson/arm64/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "arm64" */
#define SIMDJSON_IMPLEMENTATION arm64
/* including simdjson/arm64/base.h: #include "simdjson/arm64/base.h" */
/* begin file simdjson/arm64/base.h */
#ifndef SIMDJSON_ARM64_BASE_H
#define SIMDJSON_ARM64_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for NEON (ARMv8).
*/
namespace arm64 {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_BASE_H
/* end file simdjson/arm64/base.h */
/* including simdjson/arm64/intrinsics.h: #include "simdjson/arm64/intrinsics.h" */
/* begin file simdjson/arm64/intrinsics.h */
#ifndef SIMDJSON_ARM64_INTRINSICS_H
#define SIMDJSON_ARM64_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// This should be the correct header whether
// you use visual studio or other compilers.
#include <arm_neon.h>
static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient padding for arm64");
#endif // SIMDJSON_ARM64_INTRINSICS_H
/* end file simdjson/arm64/intrinsics.h */
/* including simdjson/arm64/bitmanipulation.h: #include "simdjson/arm64/bitmanipulation.h" */
/* begin file simdjson/arm64/bitmanipulation.h */
#ifndef SIMDJSON_ARM64_BITMANIPULATION_H
#define SIMDJSON_ARM64_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
// We sometimes call leading_zeroes on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true.
// (See below.)
SIMDJSON_NO_SANITIZE_UNDEFINED
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return vaddv_u8(vcnt_u8(vcreate_u8(input_num)));
#else
// if the system supports SVE or CSSC, __builtin_popcountll
// might be compiled to fewer single instructions. For CSSC,
// __builtin_popcountll is compiled to a single instruction.
return __builtin_popcountll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if defined(__GNUC__) // catches clang and gcc
/**
* ARM has a fast 64-bit "bit reversal function" that is handy. However,
* it is not generally available as an intrinsic function under Visual
* Studio (though this might be changing). Even under clang/gcc, we
* apparently need to invoke inline assembly.
*/
/*
* We use SIMDJSON_PREFER_REVERSE_BITS as a hint that algorithms that
* work well with bit reversal may use it.
*/
#define SIMDJSON_PREFER_REVERSE_BITS 1
/* reverse the bits */
simdjson_inline uint64_t reverse_bits(uint64_t input_num) {
uint64_t rev_bits;
__asm("rbit %0, %1" : "=r"(rev_bits) : "r"(input_num));
return rev_bits;
}
/**
* Flips bit at index 63 - lz. Thus if you have 'leading_zeroes' leading zeroes,
* then this will set to zero the leading bit. It is possible for leading_zeroes to be
* greating or equal to 63 in which case we trigger undefined behavior, but the output
* of such undefined behavior is never used.
**/
SIMDJSON_NO_SANITIZE_UNDEFINED
simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) {
return rev_bits ^ (uint64_t(0x8000000000000000) >> leading_zeroes);
}
#endif
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_BITMANIPULATION_H
/* end file simdjson/arm64/bitmanipulation.h */
/* including simdjson/arm64/bitmask.h: #include "simdjson/arm64/bitmask.h" */
/* begin file simdjson/arm64/bitmask.h */
#ifndef SIMDJSON_ARM64_BITMASK_H
#define SIMDJSON_ARM64_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
/////////////
// We could do this with PMULL, but it is apparently slow.
//
//#ifdef __ARM_FEATURE_CRYPTO // some ARM processors lack this extension
//return vmull_p64(-1ULL, bitmask);
//#else
// Analysis by @sebpop:
// When diffing the assembly for src/stage1_find_marks.cpp I see that the eors are all spread out
// in between other vector code, so effectively the extra cycles of the sequence do not matter
// because the GPR units are idle otherwise and the critical path is on the FP side.
// Also the PMULL requires two extra fmovs: GPR->FP (3 cycles in N1, 5 cycles in A72 )
// and FP->GPR (2 cycles on N1 and 5 cycles on A72.)
///////////
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif
/* end file simdjson/arm64/bitmask.h */
/* including simdjson/arm64/numberparsing_defs.h: #include "simdjson/arm64/numberparsing_defs.h" */
/* begin file simdjson/arm64/numberparsing_defs.h */
#ifndef SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
#define SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#if SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
// __umulh requires intrin.h
#include <intrin.h>
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO && SIMDJSON_IS_ARM64
namespace simdjson {
namespace arm64 {
namespace numberparsing {
// we don't have SSE, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace arm64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H
/* end file simdjson/arm64/numberparsing_defs.h */
/* including simdjson/arm64/simd.h: #include "simdjson/arm64/simd.h" */
/* begin file simdjson/arm64/simd.h */
#ifndef SIMDJSON_ARM64_SIMD_H
#define SIMDJSON_ARM64_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
namespace simd {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
namespace {
// Start of private section with Visual Studio workaround
#ifndef simdjson_make_uint8x16_t
#define simdjson_make_uint8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
x13, x14, x15, x16) \
([=]() { \
uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
x9, x10, x11, x12, x13, x14, x15, x16}; \
return vld1q_u8(array); \
}())
#endif
#ifndef simdjson_make_int8x16_t
#define simdjson_make_int8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
x13, x14, x15, x16) \
([=]() { \
int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
x9, x10, x11, x12, x13, x14, x15, x16}; \
return vld1q_s8(array); \
}())
#endif
#ifndef simdjson_make_uint8x8_t
#define simdjson_make_uint8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
uint8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1_u8(array); \
}())
#endif
#ifndef simdjson_make_int8x8_t
#define simdjson_make_int8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
int8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1_s8(array); \
}())
#endif
#ifndef simdjson_make_uint16x8_t
#define simdjson_make_uint16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
uint16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1q_u16(array); \
}())
#endif
#ifndef simdjson_make_int16x8_t
#define simdjson_make_int16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
([=]() { \
int16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
return vld1q_s16(array); \
}())
#endif
// End of private section with Visual Studio workaround
} // namespace
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
template<typename T>
struct simd8;
//
// Base class of simd8<uint8_t> and simd8<bool>, both of which use uint8x16_t internally.
//
template<typename T, typename Mask=simd8<bool>>
struct base_u8 {
uint8x16_t value;
static const int SIZE = sizeof(value);
// Conversion from/to SIMD register
simdjson_inline base_u8(const uint8x16_t _value) : value(_value) {}
simdjson_inline operator const uint8x16_t&() const { return this->value; }
simdjson_inline operator uint8x16_t&() { return this->value; }
// Bit operations
simdjson_inline simd8<T> operator|(const simd8<T> other) const { return vorrq_u8(*this, other); }
simdjson_inline simd8<T> operator&(const simd8<T> other) const { return vandq_u8(*this, other); }
simdjson_inline simd8<T> operator^(const simd8<T> other) const { return veorq_u8(*this, other); }
simdjson_inline simd8<T> bit_andnot(const simd8<T> other) const { return vbicq_u8(*this, other); }
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
simdjson_inline simd8<T>& operator|=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline simd8<T>& operator&=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline simd8<T>& operator^=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return vceqq_u8(lhs, rhs); }
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return vextq_u8(prev_chunk, *this, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base_u8<bool> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
static simdjson_inline simd8<bool> splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); }
simdjson_inline simd8(const uint8x16_t _value) : base_u8<bool>(_value) {}
// False constructor
simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {}
// Splat constructor
simdjson_inline simd8(bool _value) : simd8(splat(_value)) {}
// We return uint32_t instead of uint16_t because that seems to be more efficient for most
// purposes (cutting it down to uint16_t costs performance in some compilers).
simdjson_inline uint32_t to_bitmask() const {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
const uint8x16_t bit_mask = simdjson_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
#else
const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
#endif
auto minput = *this & bit_mask;
uint8x16_t tmp = vpaddq_u8(minput, minput);
tmp = vpaddq_u8(tmp, tmp);
tmp = vpaddq_u8(tmp, tmp);
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
// Integer umaxv, not a float compare: flush-to-zero would treat a denormal as zero.
simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base_u8<uint8_t> {
static simdjson_inline uint8x16_t splat(uint8_t _value) { return vmovq_n_u8(_value); }
static simdjson_inline uint8x16_t zero() { return vdupq_n_u8(0); }
static simdjson_inline uint8x16_t load(const uint8_t* values) { return vld1q_u8(values); }
simdjson_inline simd8(const uint8x16_t _value) : base_u8<uint8_t>(_value) {}
// Zero constructor
simdjson_inline simd8() : simd8(zero()) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[16]) : simd8(load(values)) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Member-by-member initialization
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(simdjson_make_uint8x16_t(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
#else
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(uint8x16_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
#endif
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Store to array
simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); }
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return vqaddq_u8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return vqsubq_u8(*this, other); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<uint8_t> operator+(const simd8<uint8_t> other) const { return vaddq_u8(*this, other); }
simdjson_inline simd8<uint8_t> operator-(const simd8<uint8_t> other) const { return vsubq_u8(*this, other); }
simdjson_inline simd8<uint8_t>& operator+=(const simd8<uint8_t> other) { *this = *this + other; return *this; }
simdjson_inline simd8<uint8_t>& operator-=(const simd8<uint8_t> other) { *this = *this - other; return *this; }
// Order-specific operations
simdjson_inline uint8_t max_val() const { return vmaxvq_u8(*this); }
simdjson_inline uint8_t min_val() const { return vminvq_u8(*this); }
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return vmaxq_u8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return vminq_u8(*this, other); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return vcleq_u8(*this, other); }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return vcgeq_u8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return vcltq_u8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return vcgtq_u8(*this, other); }
// Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's.
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this > other); }
// Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's.
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this < other); }
// Bit-specific operations
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return vtstq_u8(*this, bits); }
simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return vshrq_n_u8(*this, N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return vshlq_n_u8(*this, N); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint16_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]};
uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64);
// we increment by 0x08 the second half of the mask
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint8x16_t inc = simdjson_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
#else
uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
#endif
shufmask = vaddq_u8(shufmask, inc);
// this is the version "nearly pruned"
uint8x16_t pruned = vqtbl1q_u8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
uint8x16_t compactmask = vld1q_u8(reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
uint8x16_t answer = vqtbl1q_u8(pruned, compactmask);
vst1q_u8(reinterpret_cast<uint8_t*>(output), answer);
}
// Copies all bytes corresponding to a 0 in the low half of the mask (interpreted as a
// bitset) to output1, then those corresponding to a 0 in the high half to output2.
template<typename L>
simdjson_inline void compress_halves(uint16_t mask, L *output1, L *output2) const {
using internal::thintable_epi8;
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
uint8x8_t compactmask1 = vcreate_u8(thintable_epi8[mask1]);
uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]);
// we increment by 0x08 the second half of the mask
#if SIMDJSON_REGULAR_VISUAL_STUDIO
uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08);
#else
uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
#endif
compactmask2 = vadd_u8(compactmask2, inc);
// store each result (with the second store possibly overlapping the first)
vst1_u8((uint8_t*)output1, vqtbl1_u8(*this, compactmask1));
vst1_u8((uint8_t*)output2, vqtbl1_u8(*this, compactmask2));
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
template<typename T>
simdjson_inline simd8<uint8_t> apply_lookup_16_to(const simd8<T> original) {
return vqtbl1q_u8(*this, simd8<uint8_t>(original));
}
};
// Signed bytes
template<>
struct simd8<int8_t> {
int8x16_t value;
static simdjson_inline simd8<int8_t> splat(int8_t _value) { return vmovq_n_s8(_value); }
static simdjson_inline simd8<int8_t> zero() { return vdupq_n_s8(0); }
static simdjson_inline simd8<int8_t> load(const int8_t values[16]) { return vld1q_s8(values); }
// Conversion from/to SIMD register
simdjson_inline simd8(const int8x16_t _value) : value{_value} {}
simdjson_inline operator const int8x16_t&() const { return this->value; }
simdjson_inline operator int8x16_t&() { return this->value; }
// Zero constructor
simdjson_inline simd8() : simd8(zero()) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(simdjson_make_int8x16_t(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
#else
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(int8x16_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
#endif
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Store to array
simdjson_inline void store(int8_t dst[16]) const { return vst1q_s8(dst, *this); }
// Explicit conversion to/from unsigned
//
// Under Visual Studio/ARM64 uint8x16_t and int8x16_t are apparently the same type.
// In theory, we could check this occurrence with std::same_as and std::enabled_if but it is C++14
// and relatively ugly and hard to read.
#ifndef SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {}
#endif
simdjson_inline explicit operator simd8<uint8_t>() const { return vreinterpretq_u8_s8(this->value); }
// Math
simdjson_inline simd8<int8_t> operator+(const simd8<int8_t> other) const { return vaddq_s8(*this, other); }
simdjson_inline simd8<int8_t> operator-(const simd8<int8_t> other) const { return vsubq_s8(*this, other); }
simdjson_inline simd8<int8_t>& operator+=(const simd8<int8_t> other) { *this = *this + other; return *this; }
simdjson_inline simd8<int8_t>& operator-=(const simd8<int8_t> other) { *this = *this - other; return *this; }
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return vmaxq_s8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return vminq_s8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return vcgtq_s8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return vcltq_s8(*this, other); }
simdjson_inline simd8<bool> operator==(const simd8<int8_t> other) const { return vceqq_s8(*this, other); }
template<int N=1>
simdjson_inline simd8<int8_t> prev(const simd8<int8_t> prev_chunk) const {
return vextq_s8(prev_chunk, *this, 16 - N);
}
// Perform a lookup assuming no value is larger than 16
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return lookup_table.apply_lookup_16_to(*this);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
template<typename T>
simdjson_inline simd8<int8_t> apply_lookup_16_to(const simd8<T> original) {
return vqtbl1q_s8(*this, simd8<uint8_t>(original));
}
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint64_t popcounts = vget_lane_u64(vreinterpret_u64_u8(vcnt_u8(vcreate_u8(~mask))), 0);
// compute the prefix sum of the popcounts of each byte
uint64_t offsets = popcounts * 0x0101010101010101;
this->chunks[0].compress_halves(uint16_t(mask), output, &output[popcounts & 0xFF]);
this->chunks[1].compress_halves(uint16_t(mask >> 16), &output[(offsets >> 8) & 0xFF], &output[(offsets >> 16) & 0xFF]);
this->chunks[2].compress_halves(uint16_t(mask >> 32), &output[(offsets >> 24) & 0xFF], &output[(offsets >> 32) & 0xFF]);
this->chunks[3].compress_halves(uint16_t(mask >> 48), &output[(offsets >> 40) & 0xFF], &output[(offsets >> 48) & 0xFF]);
return offsets >> 56;
}
simdjson_inline uint64_t to_bitmask() const {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
const uint8x16_t bit_mask = simdjson_make_uint8x16_t(
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
);
#else
const uint8x16_t bit_mask = {
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
};
#endif
// Add each of the elements next to each other, successively, to stuff each 8 byte mask into one.
uint8x16_t sum0 = vpaddq_u8(this->chunks[0] & bit_mask, this->chunks[1] & bit_mask);
uint8x16_t sum1 = vpaddq_u8(this->chunks[2] & bit_mask, this->chunks[3] & bit_mask);
sum0 = vpaddq_u8(sum0, sum1);
sum0 = vpaddq_u8(sum0, sum0);
return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_SIMD_H
/* end file simdjson/arm64/simd.h */
/* including simdjson/arm64/stringparsing_defs.h: #include "simdjson/arm64/stringparsing_defs.h" */
/* begin file simdjson/arm64/stringparsing_defs.h */
#ifndef SIMDJSON_ARM64_STRINGPARSING_DEFS_H
#define SIMDJSON_ARM64_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on ARM; therefore, we
// smash them together into a 64-byte mask and get the bitmask from there.
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask64()
};
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_ARM64_STRINGPARSING_DEFS_H
/* end file simdjson/arm64/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/arm64/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for arm64: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for arm64 */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for arm64: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::arm64::number_type */
using number_type = simdjson::arm64::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for arm64 */
/* including simdjson/generic/ondemand/deserialize.h for arm64: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for arm64 */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<arm64::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<arm64::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<arm64::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<arm64::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = arm64::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = arm64::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = arm64::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = arm64::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = arm64::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = arm64::ondemand::array;
using object_type = arm64::ondemand::object;
using value_type = arm64::ondemand::value;
using document_type = arm64::ondemand::document;
using document_reference_type = arm64::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for arm64 */
/* including simdjson/generic/ondemand/value_iterator.h for arm64: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::value_iterator> : public arm64::implementation_simdjson_result_base<arm64::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(arm64::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for arm64 */
/* including simdjson/generic/ondemand/value.h for arm64: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::value> : public arm64::implementation_simdjson_result_base<arm64::ondemand::value> {
public:
simdjson_inline simdjson_result(arm64::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<arm64::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator arm64::ondemand::array() noexcept(false);
simdjson_inline operator arm64::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator arm64::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<arm64::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<arm64::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<arm64::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<arm64::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<arm64::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<arm64::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<arm64::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<arm64::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<arm64::ondemand::value>::get<arm64::ondemand::value>(
arm64::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<arm64::ondemand::value>
simdjson_result<arm64::ondemand::value>::get<arm64::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for arm64 */
/* including simdjson/generic/ondemand/logger.h for arm64: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for arm64 */
/* including simdjson/generic/ondemand/token_iterator.h for arm64: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::token_iterator> : public arm64::implementation_simdjson_result_base<arm64::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(arm64::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for arm64 */
/* including simdjson/generic/ondemand/json_iterator.h for arm64: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::json_iterator> : public arm64::implementation_simdjson_result_base<arm64::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(arm64::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for arm64 */
/* including simdjson/generic/ondemand/json_type.h for arm64: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::json_type> : public arm64::implementation_simdjson_result_base<arm64::ondemand::json_type> {
public:
simdjson_inline simdjson_result(arm64::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for arm64 */
/* including simdjson/generic/ondemand/raw_json_string.h for arm64: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::raw_json_string> : public arm64::implementation_simdjson_result_base<arm64::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(arm64::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(arm64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(arm64::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for arm64 */
/* including simdjson/generic/ondemand/parser.h for arm64: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::parser> : public arm64::implementation_simdjson_result_base<arm64::ondemand::parser> {
public:
simdjson_inline simdjson_result(arm64::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for arm64 */
// All other declarations
/* including simdjson/generic/ondemand/array.h for arm64: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::array> : public arm64::implementation_simdjson_result_base<arm64::ondemand::array> {
public:
simdjson_inline simdjson_result(arm64::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, arm64::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, arm64::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, arm64::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, arm64::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for arm64 */
/* including simdjson/generic/ondemand/array_iterator.h for arm64: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::array_iterator> : public arm64::implementation_simdjson_result_base<arm64::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<arm64::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(arm64::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<arm64::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<arm64::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<arm64::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for arm64 */
/* including simdjson/generic/ondemand/document.h for arm64: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::document> : public arm64::implementation_simdjson_result_base<arm64::ondemand::document> {
public:
simdjson_inline simdjson_result(arm64::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using arm64::implementation_simdjson_result_base<arm64::ondemand::document>::operator*;
using arm64::implementation_simdjson_result_base<arm64::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, arm64::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator arm64::ondemand::array() & noexcept(false);
simdjson_inline operator arm64::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator arm64::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator arm64::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<arm64::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<arm64::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::document_reference> : public arm64::implementation_simdjson_result_base<arm64::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(arm64::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator arm64::ondemand::array() & noexcept(false);
simdjson_inline operator arm64::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator arm64::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator arm64::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<arm64::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<arm64::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for arm64 */
/* including simdjson/generic/ondemand/document_stream.h for arm64: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace arm64 {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::document_stream> : public arm64::implementation_simdjson_result_base<arm64::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(arm64::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for arm64 */
/* including simdjson/generic/ondemand/field.h for arm64: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::field> : public arm64::implementation_simdjson_result_base<arm64::ondemand::field> {
public:
simdjson_inline simdjson_result(arm64::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<arm64::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for arm64 */
/* including simdjson/generic/ondemand/key_selector.h for arm64: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace arm64 {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for arm64 */
/* including simdjson/generic/ondemand/object.h for arm64: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::object> : public arm64::implementation_simdjson_result_base<arm64::ondemand::object> {
public:
simdjson_inline simdjson_result(arm64::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<arm64::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<arm64::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<arm64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<arm64::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(arm64::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, arm64::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, arm64::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, arm64::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, arm64::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires arm64::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, arm64::ondemand::value>
simdjson_inline arm64::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, arm64::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires arm64::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(arm64::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline arm64::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(arm64::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(arm64::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline arm64::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(arm64::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for arm64 */
/* including simdjson/generic/ondemand/object_iterator.h for arm64: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<arm64::ondemand::object_iterator> : public arm64::implementation_simdjson_result_base<arm64::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(arm64::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<arm64::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<arm64::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<arm64::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<arm64::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for arm64 */
/* including simdjson/generic/ondemand/ranges.h for arm64: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::arm64::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::arm64::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for arm64 */
/* including simdjson/generic/ondemand/serialization.h for arm64: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, arm64::ondemand::document> &&
!std::same_as<T, arm64::ondemand::value> &&
!std::same_as<T, arm64::ondemand::object> &&
!std::same_as<T, arm64::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace arm64 { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::object> x);
#endif
}}} // namespace simdjson::arm64::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for arm64 */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for arm64: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for arm64 */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
arm64::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, arm64::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
arm64::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, arm64::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
arm64::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, arm64::ondemand::value &val, T &out) noexcept(false) {
arm64::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, arm64::ondemand::document &doc, T &out) noexcept(false) {
arm64::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, arm64::ondemand::document_reference &doc, T &out) noexcept(false) {
arm64::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, arm64::ondemand::number>
&& !std::is_same_v<T, arm64::ondemand::document>
&& !std::is_same_v<T, arm64::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^arm64::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = arm64::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, arm64::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
arm64::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
arm64::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
arm64::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
arm64::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, arm64::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
arm64::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, arm64::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, arm64::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, arm64::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for arm64 */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for arm64: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::array>::simdjson_result(
arm64::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<arm64::ondemand::array>(
std::forward<arm64::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<arm64::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<arm64::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<arm64::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<arm64::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for arm64 */
/* including simdjson/generic/ondemand/array_iterator-inl.h for arm64: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::array_iterator>::simdjson_result(
arm64::ondemand::array_iterator &&value
) noexcept
: arm64::implementation_simdjson_result_base<arm64::ondemand::array_iterator>(std::forward<arm64::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: arm64::implementation_simdjson_result_base<arm64::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<arm64::ondemand::array_iterator>::operator==(const simdjson_result<arm64::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<arm64::ondemand::array_iterator>::operator!=(const simdjson_result<arm64::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> &simdjson_result<arm64::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<arm64::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for arm64 */
/* including simdjson/generic/ondemand/value-inl.h for arm64: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace arm64 {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::value>::simdjson_result(
arm64::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<arm64::ondemand::value>(
std::forward<arm64::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<arm64::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<arm64::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<arm64::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<arm64::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<arm64::ondemand::array> simdjson_result<arm64::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<arm64::ondemand::object> simdjson_result<arm64::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<arm64::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<arm64::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<arm64::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<arm64::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<arm64::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<arm64::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<arm64::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<arm64::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<arm64::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<arm64::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<arm64::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<arm64::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<arm64::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<arm64::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<arm64::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<arm64::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<arm64::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<arm64::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string> simdjson_result<arm64::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<arm64::ondemand::value>::get<arm64::ondemand::value>(arm64::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<arm64::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<arm64::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::get<arm64::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<arm64::ondemand::json_type> simdjson_result<arm64::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<arm64::number_type> simdjson_result<arm64::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<arm64::ondemand::number> simdjson_result<arm64::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<arm64::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<arm64::ondemand::value>::operator arm64::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::value>::operator arm64::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::value>::operator arm64::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<arm64::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<arm64::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<arm64::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for arm64 */
/* including simdjson/generic/ondemand/document-inl.h for arm64: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace arm64 {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::document>::simdjson_result(
arm64::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<arm64::ondemand::document>(
std::forward<arm64::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<arm64::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<arm64::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<arm64::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<arm64::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<arm64::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<arm64::ondemand::array> simdjson_result<arm64::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<arm64::ondemand::object> simdjson_result<arm64::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<arm64::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<arm64::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<arm64::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<arm64::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<arm64::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<arm64::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<arm64::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<arm64::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<arm64::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<arm64::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<arm64::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<arm64::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<arm64::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<arm64::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<arm64::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<arm64::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<arm64::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string> simdjson_result<arm64::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<arm64::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<arm64::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<arm64::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<arm64::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<arm64::ondemand::document> simdjson_result<arm64::ondemand::document>::get<arm64::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<arm64::ondemand::document> simdjson_result<arm64::ondemand::document>::get<arm64::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<arm64::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document>::get<arm64::ondemand::document>(arm64::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document>::get<arm64::ondemand::document>(arm64::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<arm64::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<arm64::ondemand::json_type> simdjson_result<arm64::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<arm64::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<arm64::number_type> simdjson_result<arm64::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<arm64::ondemand::number> simdjson_result<arm64::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, arm64::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<arm64::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator arm64::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator arm64::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator arm64::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<arm64::ondemand::document>::operator arm64::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<arm64::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<arm64::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<arm64::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<arm64::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace arm64 {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::simdjson_result(arm64::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<arm64::ondemand::document_reference>(std::forward<arm64::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<arm64::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<arm64::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<arm64::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<arm64::ondemand::array_iterator> simdjson_result<arm64::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<arm64::ondemand::array> simdjson_result<arm64::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<arm64::ondemand::object> simdjson_result<arm64::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<arm64::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<arm64::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<arm64::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<arm64::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<arm64::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<arm64::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<arm64::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<arm64::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<arm64::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<arm64::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<arm64::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<arm64::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<arm64::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<arm64::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<arm64::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<arm64::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<arm64::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string> simdjson_result<arm64::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<arm64::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<arm64::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<arm64::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, arm64::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<arm64::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<arm64::ondemand::json_type> simdjson_result<arm64::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document_reference>::get(arm64::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document_reference>::get(arm64::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<arm64::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<arm64::number_type> simdjson_result<arm64::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<arm64::ondemand::number> simdjson_result<arm64::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, arm64::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, arm64::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator arm64::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator arm64::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator arm64::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<arm64::ondemand::document_reference>::operator arm64::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<arm64::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<arm64::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for arm64 */
/* including simdjson/generic/ondemand/document_stream-inl.h for arm64: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace arm64 {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<arm64::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<arm64::ondemand::document_stream>::simdjson_result(
arm64::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<arm64::ondemand::document_stream>(
std::forward<arm64::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for arm64 */
/* including simdjson/generic/ondemand/field-inl.h for arm64: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::field>::simdjson_result(
arm64::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<arm64::ondemand::field>(
std::forward<arm64::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<arm64::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<arm64::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string> simdjson_result<arm64::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<arm64::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<arm64::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<arm64::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for arm64 */
/* including simdjson/generic/ondemand/json_iterator-inl.h for arm64: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::json_iterator>::simdjson_result(arm64::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<arm64::ondemand::json_iterator>(std::forward<arm64::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<arm64::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<arm64::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for arm64 */
/* including simdjson/generic/ondemand/json_type-inl.h for arm64: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::json_type>::simdjson_result(arm64::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<arm64::ondemand::json_type>(std::forward<arm64::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<arm64::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<arm64::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for arm64 */
/* including simdjson/generic/ondemand/logger-inl.h for arm64: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace arm64 {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for arm64 */
/* including simdjson/generic/ondemand/object-inl.h for arm64: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::object>::simdjson_result(arm64::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<arm64::ondemand::object>(std::forward<arm64::ondemand::object>(value)) {}
simdjson_inline simdjson_result<arm64::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<arm64::ondemand::object>(error) {}
simdjson_inline simdjson_result<arm64::ondemand::object_iterator> simdjson_result<arm64::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<arm64::ondemand::object_iterator> simdjson_result<arm64::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<arm64::ondemand::object_iterator> simdjson_result<arm64::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<arm64::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<arm64::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<arm64::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<arm64::ondemand::value> simdjson_result<arm64::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, arm64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<arm64::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires arm64::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, arm64::ondemand::value>
simdjson_inline arm64::ondemand::for_each_result
simdjson_result<arm64::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, arm64::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires arm64::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(arm64::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline arm64::ondemand::for_each_result
simdjson_result<arm64::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(arm64::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(arm64::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline arm64::ondemand::for_each_result
simdjson_result<arm64::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(arm64::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<arm64::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<arm64::ondemand::object_position> simdjson_result<arm64::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<arm64::ondemand::object>::revert_position(arm64::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<arm64::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<arm64::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<arm64::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for arm64 */
/* including simdjson/generic/ondemand/object_iterator-inl.h for arm64: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::object_iterator>::simdjson_result(
arm64::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<arm64::ondemand::object_iterator>(std::forward<arm64::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<arm64::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<arm64::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<arm64::ondemand::field> simdjson_result<arm64::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<arm64::ondemand::object_iterator>::operator==(const simdjson_result<arm64::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<arm64::ondemand::object_iterator>::operator!=(const simdjson_result<arm64::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<arm64::ondemand::object_iterator> &simdjson_result<arm64::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for arm64 */
/* including simdjson/generic/ondemand/ranges-inl.h for arm64: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace arm64 {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::arm64::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::arm64::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::arm64::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::arm64::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::arm64::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::arm64::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for arm64 */
/* including simdjson/generic/ondemand/parser-inl.h for arm64: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::parser>::simdjson_result(arm64::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<arm64::ondemand::parser>(std::forward<arm64::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<arm64::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<arm64::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for arm64 */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for arm64: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string>::simdjson_result(arm64::ondemand::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<arm64::ondemand::raw_json_string>(std::forward<arm64::ondemand::raw_json_string>(value)) {}
simdjson_inline simdjson_result<arm64::ondemand::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<arm64::ondemand::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<arm64::ondemand::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline char simdjson_result<arm64::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
if (error()) { return error(); }
return first[i];
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<arm64::ondemand::raw_json_string>::unescape(arm64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<arm64::ondemand::raw_json_string>::unescape_wobbly(arm64::ondemand::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* end file simdjson/generic/ondemand/raw_json_string-inl.h for arm64 */
/* including simdjson/generic/ondemand/token_iterator-inl.h for arm64: #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/token_iterator-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::token_iterator>::simdjson_result(arm64::ondemand::token_iterator &&value) noexcept
: implementation_simdjson_result_base<arm64::ondemand::token_iterator>(std::forward<arm64::ondemand::token_iterator>(value)) {}
simdjson_inline simdjson_result<arm64::ondemand::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<arm64::ondemand::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/token_iterator-inl.h for arm64 */
/* including simdjson/generic/ondemand/value_iterator-inl.h for arm64: #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/value_iterator-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace arm64 {
namespace ondemand {
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
bool escaping{false};
for (uint32_t i = 1; i < max_len; i++) {
switch (json[i]) {
case '"':
if (!escaping) { return true; }
escaping = false;
break;
case '\\':
escaping = !escaping;
break;
default:
escaping = false;
break;
}
}
return false;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
SIMDJSON_TRY(end_container());
return false;
}
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
// call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != '}') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
// If the last character is } *and* the first gibberish character is also '}'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed object.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
auto error = check_root_object();
if(error) { return error; }
return started_object();
}
simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
#if SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
return false;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return false;
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
// If someone queried a key but they not did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
//
// ```
// // At the beginning of a field
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // At the end of the object
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// ```
//
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
SIMDJSON_TRY(reset_object().get(has_value));
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
return raw_json_string(key);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
assert_at_next();
const uint8_t *k = _json_iter->return_current_and_advance();
if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
// After return_current_and_advance(), the current token is the ':' that follows
// the key. The closing quote sits just before it (only JSON whitespace may
// intervene), so step back from the ':' to the closing quote to get the length.
// In minified JSON this is a single back-step.
const char *q = reinterpret_cast<const char *>(_json_iter->peek());
do { --q; } while (*q != '"');
key = raw_json_string(k);
len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
assert_at_next();
if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
answer += std::string(" ]");
return answer;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
SIMDJSON_TRY( end_container() );
return false;
}
_json_iter->descend_to(depth()+1);
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
// also call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != ']') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
// If the last character is ] *and* the first gibberish character is also ']'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed array.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
auto error = check_root_array();
if (error) { return error; }
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
}
simdjson_inline bool value_iterator::is_root_negative() noexcept {
return numberparsing::is_negative(peek_root_scalar("numbersign"));
}
simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
return numberparsing::is_integer(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
return numberparsing::get_number_type(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
number num;
error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
if(error) { return error; }
return num;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<arm64::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_inline value_iterator value_iterator::child() const noexcept {
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool value_iterator::at_end() const noexcept {
return _json_iter->at_end();
}
simdjson_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.position() == start_position();
}
simdjson_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
return _json_iter->error;
}
simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
return _json_iter->string_buf_loc();
}
simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
return *_json_iter;
}
simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
return *_json_iter;
}
simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(start_position());
}
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
logger::log_start_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
const uint8_t *json;
if (!is_at_start()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
return SUCCESS;
}
simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
return _json_iter->peek();
}
simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
logger::log_error(*_json_iter, start_position(), depth(), message);
return INCORRECT_TYPE;
}
simdjson_inline bool value_iterator::is_at_start() const noexcept {
return position() == start_position();
}
simdjson_inline bool value_iterator::is_at_key() const noexcept {
// Keys are at the same depth as the object.
// Note here that we could be safer and check that we are within an object,
// but we do not.
//
// As long as we are at the object's depth, in a valid document,
// we will only ever be at { , : or the actual string key: ".
return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
}
simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = position() - start_position();
return delta == 1 || delta == 2;
}
inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_inline void value_iterator::move_at_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position);
}
simdjson_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
// Unlike reenter_child(), this does not require the live depth to be
// exactly one level shallower than depth, nor does it validate against
// the parser's per-depth container-start bookkeeping: neither holds in
// general for a caller-supplied snapshot (see object_position). What
// must still always hold, regardless of what was captured or how far
// the live iterator has since moved, is that position and depth are
// themselves sane values -- this is the same bound reenter_child()
// itself applies unconditionally.
SIMDJSON_ASSUME(position != nullptr);
SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
_json_iter->_depth = depth;
_json_iter->token.set_position(position);
}
simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_array();
}
simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_object();
}
inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return json_type::unknown;
}
}
simdjson_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<arm64::ondemand::value_iterator>::simdjson_result(arm64::ondemand::value_iterator &&value) noexcept
: implementation_simdjson_result_base<arm64::ondemand::value_iterator>(std::forward<arm64::ondemand::value_iterator>(value)) {}
simdjson_inline simdjson_result<arm64::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<arm64::ondemand::value_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/value_iterator-inl.h for arm64 */
/* including simdjson/generic/ondemand/serialization-inl.h for arm64: #include "simdjson/generic/ondemand/serialization-inl.h" */
/* begin file simdjson/generic/ondemand/serialization-inl.h for arm64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-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/serialization.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace arm64::ondemand;
arm64::ondemand::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
arm64::ondemand::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
arm64::ondemand::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(arm64::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<arm64::ondemand::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace arm64 { namespace ondemand {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::arm64::ondemand::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::arm64::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::arm64::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* end file simdjson/generic/ondemand/serialization-inl.h for arm64 */
// JSON path accessor (compile-time) - must be after inline definitions
/* including simdjson/generic/ondemand/compile_time_accessors.h for arm64: #include "simdjson/generic/ondemand/compile_time_accessors.h" */
/* begin file simdjson/generic/ondemand/compile_time_accessors.h for arm64 */
/**
* Compile-time JSON Path and JSON Pointer accessors using C++26 reflection (P2996)
*
* This file validates JSON paths/pointers against struct definitions at compile time
* and generates optimized accessor code with zero runtime overhead.
*
* ## How It Works
*
* **Compile Time**: Path is parsed, validated against struct, types are checked
* **Runtime**: Direct navigation with no parsing or validation overhead
*
* Example:
* ```cpp
* struct User { std::string name; std::vector<std::string> emails; };
*
* std::string email;
* path_accessor<User, ".emails[0]">::extract_field(doc, email);
*
* // Compile time validates:
* // 1. User has "emails" field
* // 2. "emails" is array-like
* // 3. Element type is std::string
* // 4. static_assert(^^std::string == ^^std::string)
*
* // Runtime just navigates:
* // doc.get_object().find_field("emails").get_array().at(0).get(email)
* ```
*
* ## Key Reflection APIs
*
* - `^^Type`: Reflect operator, converts type to std::meta::info
* - `std::meta::nonstatic_data_members_of(type)`: Get all fields of a struct
* - `std::meta::identifier_of(member)`: Get field name as string_view
* - `std::meta::type_of(member)`: Get reflected type of a field
* - `std::meta::is_array_type(type)`: Check if C-style array
* - `std::meta::remove_extent(array)`: Extract element type from array
* - `std::meta::members_of(type)`: Get all members including typedefs
* - `std::meta::is_type(member)`: Check if member is a type (vs field)
*
* All operations execute at compile time in consteval contexts.
*/
#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H */
/* amalgamation skipped (editor-only): */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
// is unlikely that we will have reflection support without concepts support.
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#include <string_view>
#include <cstddef>
#include <array>
namespace simdjson {
namespace arm64 {
namespace ondemand {
/***
* JSONPath implementation for compile-time access
* RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
*/
namespace json_path {
// Note: value type must be fully defined before this header is included
// This is ensured by including this in amalgamated.h after value-inl.h
using ::simdjson::arm64::ondemand::value;
// Path step types
enum class step_type {
field, // .field_name or ["field_name"]
array_index // [index]
};
// Represents a single step in a JSON path
template<std::size_t N>
struct path_step {
step_type type;
char key[N]; // Field name (empty for array indices)
std::size_t index; // Array index (0 for field access)
constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
: type(t), index(idx) {
for (std::size_t i = 0; i < N; ++i) {
key[i] = k[i];
}
}
constexpr std::string_view key_view() const {
return {key, N - 1};
}
};
// Helper to create field step
template<std::size_t N>
consteval auto make_field_step(const char (&name)[N]) {
return path_step<N>(step_type::field, name, 0);
}
// Helper to create array index step
consteval auto make_index_step(std::size_t idx) {
return path_step<1>(step_type::array_index, "", idx);
}
// Parse state for compile-time JSON path parsing
struct parse_result {
bool success;
std::size_t pos;
std::string_view error_msg;
};
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
struct json_path_parser {
static constexpr std::string_view path_str = Path.view();
// Skip leading $ if present
static consteval std::size_t skip_root() {
if (!path_str.empty() && path_str[0] == '$') {
return 1;
}
return 0;
}
// Count the number of steps in the path at compile time
static consteval std::size_t count_steps() {
std::size_t count = 0;
std::size_t i = skip_root();
while (i < path_str.size()) {
if (path_str[i] == '.') {
// Field access: .field
++i;
if (i >= path_str.size()) break;
// Skip field name
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
++i;
}
++count;
} else if (path_str[i] == '[') {
// Array or bracket notation
++i;
if (i >= path_str.size()) break;
if (path_str[i] == '"' || path_str[i] == '\'') {
// Field access: ["field"] or ['field']
char quote = path_str[i];
++i;
while (i < path_str.size() && path_str[i] != quote) {
++i;
}
if (i < path_str.size()) ++i; // skip closing quote
if (i < path_str.size() && path_str[i] == ']') ++i;
} else {
// Array index: [0], [123]
while (i < path_str.size() && path_str[i] != ']') {
++i;
}
if (i < path_str.size()) ++i; // skip ]
}
++count;
} else {
++i;
}
}
return count;
}
// Parse a field name at compile time
static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
std::size_t len = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
out[len++] = path_str[i++];
}
out[len] = '\0';
return i;
}
// Parse an array index at compile time
static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
std::size_t index = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
index = index * 10 + (path_str[i] - '0');
++i;
}
return {i, index};
}
};
// Compile-time path accessor generator
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
using value = ::simdjson::arm64::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
// If T is a struct, validates the path at compile time
// If T is void, skips validation
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
// Validate path at compile time if T is a struct
if constexpr (std::is_class_v<T>) {
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
}
// Parse the path at compile time to build access steps
return access_impl<parser.skip_root()>(doc_or_val.get_value());
}
// Extract value at path directly into target with compile-time type validation
// Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
// Validate path exists in struct definition
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
// Get the type at the end of the path
constexpr auto final_type = get_final_type();
// Verify target type matches the field type
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
// All validation done at compile time - just navigate and extract
auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get the final type by walking the path through the struct type
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
// .field syntax
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) break;
if (path_view[i] == '"' || path_view[i] == '\'') {
// ["field"] syntax
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip quote
if (i < path_view.size() && path_view[i] == ']') ++i;
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else {
// [index] syntax - extract element type
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
current_type = get_element_type_reflected(current_type);
}
} else {
++i;
}
}
return current_type;
}
private:
// Walk path and extract directly into final field using compile-time reflection
template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
if (current.error()) return current.error();
// Base case: end of path - extract into target
if constexpr (PathPos >= path_view.size()) {
return current.get(target_ref);
}
// Field access: .field_name
else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
}
// Bracket notation: [index] or ["field"]
else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
constexpr auto elem_type = get_element_type_reflected(CurrentType);
static_assert(elem_type != ^^void, "Could not determine array element type");
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto elem_value = arr.at(index);
if constexpr (next_pos >= path_view.size()) {
return elem_value.get(target_ref);
} else {
return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
}
}
}
// Skip unexpected characters and continue
else {
return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
}
}
// Find member by name in reflected type
static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == name) {
return mem;
}
}
}
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if (current.error()) return current;
if constexpr (PathPos >= path_view.size()) {
return current;
} else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto next_value = arr.at(index);
return access_impl<next_pos>(next_value);
}
} else {
return access_impl<PathPos + 1>(current);
}
}
// Parse next field name
static consteval auto parse_next_field(std::size_t start) {
std::size_t i = start + 1;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
return std::make_tuple(field_name, i);
}
// Parse bracket notation: returns (is_field, field_name, next_pos, index)
static consteval auto parse_bracket(std::size_t start) {
std::size_t i = start + 1; // skip '['
if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
// Field access
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip closing quote
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(true, field_name, i, std::size_t(0));
} else {
// Array index
std::size_t index = 0;
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
index = index * 10 + (path_view[i] - '0');
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(false, std::string_view{}, i, index);
}
}
public:
// Check if reflected type is array-like (C-style array or indexable container)
// Uses reflection to test: 1) std::meta::is_array_type() for C arrays
// 2) std::meta::substitute() to test concepts::indexable_container concept
static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return true;
}
if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
}
return false;
}
// Extract element type from reflected array or container
// For C arrays: uses std::meta::remove_extent()
// For containers: finds value_type member using std::meta::members_of()
static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return std::meta::remove_extent(type_reflection);
}
auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::is_type(mem)) {
auto name = std::meta::identifier_of(mem);
if (name == "value_type") {
return mem;
}
}
}
return ^^void;
}
private:
// Check if type has member with given name
template<typename Type>
static consteval bool has_member(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return true;
}
}
return false;
}
// Get type of member by name
template<typename Type>
static consteval auto get_member_type(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return std::meta::type_of(mem);
}
}
return ^^void;
}
// Check if non-reflected type is array-like
template<typename Type>
static consteval bool is_container_type() {
using BaseType = std::remove_cvref_t<Type>;
if constexpr (requires { typename BaseType::value_type; }) {
return true;
}
if constexpr (std::is_array_v<BaseType>) {
return true;
}
return false;
}
// Extract element type from non-reflected container
template<typename Type>
using extract_element_type = std::conditional_t<
requires { typename std::remove_cvref_t<Type>::value_type; },
typename std::remove_cvref_t<Type>::value_type,
std::conditional_t<
std::is_array_v<std::remove_cvref_t<Type>>,
std::remove_extent_t<std::remove_cvref_t<Type>>,
void
>
>;
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) return false;
if (path_view[i] == '"' || path_view[i] == '\'') {
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i;
if (i < path_view.size() && path_view[i] == ']') ++i;
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else {
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
if (!is_array_like_reflected(current_type)) {
return false;
}
auto new_type = get_element_type_reflected(current_type);
if (new_type == ^^void) {
return false;
}
current_type = new_type;
}
} else {
++i;
}
}
return true;
}
};
// Compile-time path accessor with validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::arm64::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<T, Path>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::arm64::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<void, Path>;
return accessor::access(doc_or_val);
}
// ============================================================================
// JSON Pointer Compile-Time Support (RFC 6901)
// ============================================================================
// JSON Pointer parser: /field/0/nested (slash-separated)
template<constevalutil::fixed_string Pointer>
struct json_pointer_parser {
static constexpr std::string_view pointer_str = Pointer.view();
// Unescape token: ~0 -> ~, ~1 -> /
static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
out_len = 0;
for (std::size_t i = 0; i < src.size(); ++i) {
if (src[i] == '~' && i + 1 < src.size()) {
if (src[i + 1] == '0') {
dest[out_len++] = '~';
++i;
} else if (src[i + 1] == '1') {
dest[out_len++] = '/';
++i;
} else {
dest[out_len++] = src[i];
}
} else {
dest[out_len++] = src[i];
}
}
}
// Check if token is numeric
static consteval bool is_numeric(std::string_view token) {
if (token.empty()) return false;
if (token[0] == '0' && token.size() > 1) return false;
for (char c : token) {
if (c < '0' || c > '9') return false;
}
return true;
}
// Parse numeric token to index
static consteval std::size_t parse_index(std::string_view token) {
std::size_t result = 0;
for (char c : token) {
result = result * 10 + (c - '0');
}
return result;
}
// Count tokens in pointer
static consteval std::size_t count_tokens() {
if (pointer_str.empty() || pointer_str == "/") return 0;
std::size_t count = 0;
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
while (pos < pointer_str.size()) {
++count;
std::size_t next_slash = pointer_str.find('/', pos);
if (next_slash == std::string_view::npos) break;
pos = next_slash + 1;
}
return count;
}
// Get Nth token
static consteval std::string_view get_token(std::size_t token_index) {
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
std::size_t current_token = 0;
while (current_token < token_index) {
std::size_t next_slash = pointer_str.find('/', pos);
pos = next_slash + 1;
++current_token;
}
std::size_t token_end = pointer_str.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_str.size();
return pointer_str.substr(pos, token_end - pos);
}
};
// JSON Pointer accessor
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
using parser = json_pointer_parser<Pointer>;
static constexpr std::string_view pointer_view = Pointer.view();
static constexpr std::size_t token_count = parser::count_tokens();
// Validate pointer against struct definition
static consteval bool validate_pointer() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
// Extract token up to next /
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
return false;
}
current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
} else {
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) return false;
}
pos = token_end + 1;
}
return true;
}
// Recursive accessor
template<std::size_t TokenIndex>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if constexpr (TokenIndex >= token_count) {
return current;
} else {
constexpr std::string_view token = parser::get_token(TokenIndex);
if constexpr (parser::is_numeric(token)) {
constexpr std::size_t index = parser::parse_index(token);
auto arr = current.get_array().value_unsafe();
auto next_value = arr.at(index);
return access_impl<TokenIndex + 1>(next_value);
} else {
auto obj = current.get_object().value_unsafe();
auto next_value = obj.find_field_unordered(token);
return access_impl<TokenIndex + 1>(next_value);
}
}
}
// Access JSON value at pointer
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
if constexpr (std::is_class_v<T>) {
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
}
if (pointer_view.empty() || pointer_view == "/") {
if constexpr (requires { doc_or_val.get_value(); }) {
return doc_or_val.get_value();
} else {
return doc_or_val;
}
}
simdjson_result<value> current = doc_or_val.get_value();
return access_impl<0>(current);
}
// Extract value at pointer directly into target with type validation
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
constexpr auto final_type = get_final_type();
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
simdjson_result<value> current_value = doc_or_val.get_value();
auto json_value = access_impl<0>(current_value);
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get final type by walking pointer through struct
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
} else {
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
break;
}
}
}
pos = token_end + 1;
}
return current_type;
}
};
// Compile-time JSON Pointer accessor with validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::arm64::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<T, Pointer>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::arm64::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<void, Pointer>;
return accessor::access(doc_or_val);
}
} // namespace json_path
} // namespace ondemand
} // namespace arm64
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* end file simdjson/generic/ondemand/compile_time_accessors.h for arm64 */
/* end file simdjson/generic/ondemand/amalgamated.h for arm64 */
/* including simdjson/arm64/end.h: #include "simdjson/arm64/end.h" */
/* begin file simdjson/arm64/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/arm64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "arm64" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/arm64/end.h */
#endif // SIMDJSON_ARM64_ONDEMAND_H
/* end file simdjson/arm64/ondemand.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(fallback)
/* including simdjson/fallback/ondemand.h: #include "simdjson/fallback/ondemand.h" */
/* begin file simdjson/fallback/ondemand.h */
#ifndef SIMDJSON_FALLBACK_ONDEMAND_H
#define SIMDJSON_FALLBACK_ONDEMAND_H
/* including simdjson/fallback/begin.h: #include "simdjson/fallback/begin.h" */
/* begin file simdjson/fallback/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "fallback" */
#define SIMDJSON_IMPLEMENTATION fallback
/* including simdjson/fallback/base.h: #include "simdjson/fallback/base.h" */
/* begin file simdjson/fallback/base.h */
#ifndef SIMDJSON_FALLBACK_BASE_H
#define SIMDJSON_FALLBACK_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Fallback implementation (runs on any machine).
*/
namespace fallback {
class implementation;
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_BASE_H
/* end file simdjson/fallback/base.h */
/* including simdjson/fallback/bitmanipulation.h: #include "simdjson/fallback/bitmanipulation.h" */
/* begin file simdjson/fallback/bitmanipulation.h */
#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H
#define SIMDJSON_FALLBACK_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace {
#if defined(_MSC_VER) && !defined(_M_ARM64) && !defined(_M_X64)
static inline unsigned char _BitScanForward64(unsigned long* ret, uint64_t x) {
unsigned long x0 = (unsigned long)x, top, bottom;
_BitScanForward(&top, (unsigned long)(x >> 32));
_BitScanForward(&bottom, x0);
*ret = x0 ? bottom : 32 + top;
return x != 0;
}
static unsigned char _BitScanReverse64(unsigned long* ret, uint64_t x) {
unsigned long x1 = (unsigned long)(x >> 32), top, bottom;
_BitScanReverse(&top, x1);
_BitScanReverse(&bottom, (unsigned long)x);
*ret = x1 ? top + 32 : bottom;
return x != 0;
}
#endif
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#ifdef _MSC_VER
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// _MSC_VER
}
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#ifdef _MSC_VER
unsigned long trailing_zero = 0;
// Search the mask data from least significant bit (LSB)
// to most significant bit (MSB) for a set bit (1).
if (_BitScanForward64(&trailing_zero, input_num))
return (int)trailing_zero;
else return 64;
#else
return __builtin_ctzll(input_num);
#endif// _MSC_VER
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_BITMANIPULATION_H
/* end file simdjson/fallback/bitmanipulation.h */
/* including simdjson/fallback/stringparsing_defs.h: #include "simdjson/fallback/stringparsing_defs.h" */
/* begin file simdjson/fallback/stringparsing_defs.h */
#ifndef SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
#define SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace {
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return c == '"'; }
simdjson_inline bool has_backslash() { return c == '\\'; }
simdjson_inline int quote_index() { return c == '"' ? 0 : 1; }
simdjson_inline int backslash_index() { return c == '\\' ? 0 : 1; }
uint8_t c;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// store to dest unconditionally - we can overwrite the bits we don't like later
dst[0] = src[0];
return { src[0] };
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits; }
simdjson_inline int escape_index() { return 0; }
bool escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
dst[0] = src[0];
return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_FALLBACK_STRINGPARSING_DEFS_H
/* end file simdjson/fallback/stringparsing_defs.h */
/* including simdjson/fallback/numberparsing_defs.h: #include "simdjson/fallback/numberparsing_defs.h" */
/* begin file simdjson/fallback/numberparsing_defs.h */
#ifndef SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
#define SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#ifdef JSON_TEST_NUMBERS // for unit testing
void found_invalid_number(const uint8_t *buf);
void found_integer(int64_t result, const uint8_t *buf);
void found_unsigned_integer(uint64_t result, const uint8_t *buf);
void found_float(double result, const uint8_t *buf);
#endif
namespace simdjson {
namespace fallback {
namespace numberparsing {
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) {
uint64_t val;
memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars));
}
#if SIMDJSON_IS_32BITS // _umul128 for x86, arm
// this is a slow emulation routine for 32-bit
//
static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = !!(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
(adbc_carry << 32) + !!(lo < bd);
return lo;
}
#endif
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace fallback
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H
/* end file simdjson/fallback/numberparsing_defs.h */
/* end file simdjson/fallback/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for fallback: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for fallback */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for fallback: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::fallback::number_type */
using number_type = simdjson::fallback::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for fallback */
/* including simdjson/generic/ondemand/deserialize.h for fallback: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for fallback */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<fallback::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<fallback::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<fallback::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<fallback::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = fallback::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = fallback::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = fallback::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = fallback::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = fallback::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = fallback::ondemand::array;
using object_type = fallback::ondemand::object;
using value_type = fallback::ondemand::value;
using document_type = fallback::ondemand::document;
using document_reference_type = fallback::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for fallback */
/* including simdjson/generic/ondemand/value_iterator.h for fallback: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::value_iterator> : public fallback::implementation_simdjson_result_base<fallback::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(fallback::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for fallback */
/* including simdjson/generic/ondemand/value.h for fallback: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::value> : public fallback::implementation_simdjson_result_base<fallback::ondemand::value> {
public:
simdjson_inline simdjson_result(fallback::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<fallback::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator fallback::ondemand::array() noexcept(false);
simdjson_inline operator fallback::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator fallback::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<fallback::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<fallback::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<fallback::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<fallback::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<fallback::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<fallback::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<fallback::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<fallback::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<fallback::ondemand::value>::get<fallback::ondemand::value>(
fallback::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<fallback::ondemand::value>
simdjson_result<fallback::ondemand::value>::get<fallback::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for fallback */
/* including simdjson/generic/ondemand/logger.h for fallback: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for fallback */
/* including simdjson/generic/ondemand/token_iterator.h for fallback: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::token_iterator> : public fallback::implementation_simdjson_result_base<fallback::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(fallback::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for fallback */
/* including simdjson/generic/ondemand/json_iterator.h for fallback: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::json_iterator> : public fallback::implementation_simdjson_result_base<fallback::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(fallback::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for fallback */
/* including simdjson/generic/ondemand/json_type.h for fallback: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::json_type> : public fallback::implementation_simdjson_result_base<fallback::ondemand::json_type> {
public:
simdjson_inline simdjson_result(fallback::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for fallback */
/* including simdjson/generic/ondemand/raw_json_string.h for fallback: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::raw_json_string> : public fallback::implementation_simdjson_result_base<fallback::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(fallback::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(fallback::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(fallback::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for fallback */
/* including simdjson/generic/ondemand/parser.h for fallback: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::parser> : public fallback::implementation_simdjson_result_base<fallback::ondemand::parser> {
public:
simdjson_inline simdjson_result(fallback::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for fallback */
// All other declarations
/* including simdjson/generic/ondemand/array.h for fallback: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::array> : public fallback::implementation_simdjson_result_base<fallback::ondemand::array> {
public:
simdjson_inline simdjson_result(fallback::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, fallback::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, fallback::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, fallback::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, fallback::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for fallback */
/* including simdjson/generic/ondemand/array_iterator.h for fallback: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::array_iterator> : public fallback::implementation_simdjson_result_base<fallback::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<fallback::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(fallback::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<fallback::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<fallback::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<fallback::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for fallback */
/* including simdjson/generic/ondemand/document.h for fallback: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::document> : public fallback::implementation_simdjson_result_base<fallback::ondemand::document> {
public:
simdjson_inline simdjson_result(fallback::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using fallback::implementation_simdjson_result_base<fallback::ondemand::document>::operator*;
using fallback::implementation_simdjson_result_base<fallback::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, fallback::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator fallback::ondemand::array() & noexcept(false);
simdjson_inline operator fallback::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator fallback::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator fallback::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<fallback::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<fallback::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::document_reference> : public fallback::implementation_simdjson_result_base<fallback::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(fallback::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator fallback::ondemand::array() & noexcept(false);
simdjson_inline operator fallback::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator fallback::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator fallback::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<fallback::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<fallback::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for fallback */
/* including simdjson/generic/ondemand/document_stream.h for fallback: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace fallback {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::document_stream> : public fallback::implementation_simdjson_result_base<fallback::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(fallback::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for fallback */
/* including simdjson/generic/ondemand/field.h for fallback: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::field> : public fallback::implementation_simdjson_result_base<fallback::ondemand::field> {
public:
simdjson_inline simdjson_result(fallback::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<fallback::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for fallback */
/* including simdjson/generic/ondemand/key_selector.h for fallback: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace fallback {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for fallback */
/* including simdjson/generic/ondemand/object.h for fallback: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::object> : public fallback::implementation_simdjson_result_base<fallback::ondemand::object> {
public:
simdjson_inline simdjson_result(fallback::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<fallback::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<fallback::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<fallback::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<fallback::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(fallback::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, fallback::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, fallback::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, fallback::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, fallback::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires fallback::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, fallback::ondemand::value>
simdjson_inline fallback::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, fallback::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires fallback::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(fallback::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline fallback::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(fallback::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(fallback::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline fallback::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(fallback::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for fallback */
/* including simdjson/generic/ondemand/object_iterator.h for fallback: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<fallback::ondemand::object_iterator> : public fallback::implementation_simdjson_result_base<fallback::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(fallback::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<fallback::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<fallback::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<fallback::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<fallback::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for fallback */
/* including simdjson/generic/ondemand/ranges.h for fallback: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::fallback::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::fallback::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for fallback */
/* including simdjson/generic/ondemand/serialization.h for fallback: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, fallback::ondemand::document> &&
!std::same_as<T, fallback::ondemand::value> &&
!std::same_as<T, fallback::ondemand::object> &&
!std::same_as<T, fallback::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace fallback { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::object> x);
#endif
}}} // namespace simdjson::fallback::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for fallback */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for fallback: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for fallback */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
fallback::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, fallback::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
fallback::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, fallback::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
fallback::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, fallback::ondemand::value &val, T &out) noexcept(false) {
fallback::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, fallback::ondemand::document &doc, T &out) noexcept(false) {
fallback::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, fallback::ondemand::document_reference &doc, T &out) noexcept(false) {
fallback::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, fallback::ondemand::number>
&& !std::is_same_v<T, fallback::ondemand::document>
&& !std::is_same_v<T, fallback::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^fallback::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = fallback::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, fallback::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
fallback::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
fallback::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
fallback::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
fallback::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, fallback::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
fallback::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, fallback::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, fallback::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, fallback::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for fallback */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for fallback: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::array>::simdjson_result(
fallback::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<fallback::ondemand::array>(
std::forward<fallback::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<fallback::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<fallback::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<fallback::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<fallback::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for fallback */
/* including simdjson/generic/ondemand/array_iterator-inl.h for fallback: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::array_iterator>::simdjson_result(
fallback::ondemand::array_iterator &&value
) noexcept
: fallback::implementation_simdjson_result_base<fallback::ondemand::array_iterator>(std::forward<fallback::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: fallback::implementation_simdjson_result_base<fallback::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<fallback::ondemand::array_iterator>::operator==(const simdjson_result<fallback::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<fallback::ondemand::array_iterator>::operator!=(const simdjson_result<fallback::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> &simdjson_result<fallback::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<fallback::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for fallback */
/* including simdjson/generic/ondemand/value-inl.h for fallback: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace fallback {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::value>::simdjson_result(
fallback::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<fallback::ondemand::value>(
std::forward<fallback::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<fallback::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<fallback::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<fallback::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<fallback::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<fallback::ondemand::array> simdjson_result<fallback::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<fallback::ondemand::object> simdjson_result<fallback::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<fallback::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<fallback::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<fallback::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<fallback::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<fallback::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<fallback::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<fallback::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<fallback::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<fallback::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<fallback::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<fallback::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<fallback::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<fallback::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<fallback::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<fallback::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<fallback::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<fallback::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<fallback::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> simdjson_result<fallback::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<fallback::ondemand::value>::get<fallback::ondemand::value>(fallback::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<fallback::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<fallback::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::get<fallback::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<fallback::ondemand::json_type> simdjson_result<fallback::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<fallback::number_type> simdjson_result<fallback::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<fallback::ondemand::number> simdjson_result<fallback::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<fallback::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<fallback::ondemand::value>::operator fallback::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::value>::operator fallback::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::value>::operator fallback::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<fallback::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<fallback::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<fallback::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for fallback */
/* including simdjson/generic/ondemand/document-inl.h for fallback: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace fallback {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::document>::simdjson_result(
fallback::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<fallback::ondemand::document>(
std::forward<fallback::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<fallback::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<fallback::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<fallback::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<fallback::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<fallback::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<fallback::ondemand::array> simdjson_result<fallback::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<fallback::ondemand::object> simdjson_result<fallback::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<fallback::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<fallback::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<fallback::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<fallback::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<fallback::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<fallback::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<fallback::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<fallback::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<fallback::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<fallback::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<fallback::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<fallback::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<fallback::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<fallback::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<fallback::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<fallback::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<fallback::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> simdjson_result<fallback::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<fallback::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<fallback::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<fallback::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<fallback::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<fallback::ondemand::document> simdjson_result<fallback::ondemand::document>::get<fallback::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<fallback::ondemand::document> simdjson_result<fallback::ondemand::document>::get<fallback::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<fallback::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document>::get<fallback::ondemand::document>(fallback::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document>::get<fallback::ondemand::document>(fallback::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<fallback::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<fallback::ondemand::json_type> simdjson_result<fallback::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<fallback::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<fallback::number_type> simdjson_result<fallback::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<fallback::ondemand::number> simdjson_result<fallback::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, fallback::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<fallback::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator fallback::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator fallback::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator fallback::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<fallback::ondemand::document>::operator fallback::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<fallback::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<fallback::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<fallback::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<fallback::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace fallback {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::simdjson_result(fallback::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<fallback::ondemand::document_reference>(std::forward<fallback::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<fallback::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<fallback::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<fallback::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<fallback::ondemand::array_iterator> simdjson_result<fallback::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<fallback::ondemand::array> simdjson_result<fallback::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<fallback::ondemand::object> simdjson_result<fallback::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<fallback::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<fallback::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<fallback::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<fallback::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<fallback::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<fallback::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<fallback::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<fallback::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<fallback::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<fallback::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<fallback::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<fallback::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<fallback::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<fallback::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<fallback::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<fallback::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<fallback::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> simdjson_result<fallback::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<fallback::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<fallback::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<fallback::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, fallback::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<fallback::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<fallback::ondemand::json_type> simdjson_result<fallback::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document_reference>::get(fallback::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document_reference>::get(fallback::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<fallback::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<fallback::number_type> simdjson_result<fallback::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<fallback::ondemand::number> simdjson_result<fallback::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, fallback::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, fallback::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator fallback::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator fallback::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator fallback::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<fallback::ondemand::document_reference>::operator fallback::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<fallback::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<fallback::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for fallback */
/* including simdjson/generic/ondemand/document_stream-inl.h for fallback: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace fallback {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<fallback::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<fallback::ondemand::document_stream>::simdjson_result(
fallback::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<fallback::ondemand::document_stream>(
std::forward<fallback::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for fallback */
/* including simdjson/generic/ondemand/field-inl.h for fallback: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::field>::simdjson_result(
fallback::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<fallback::ondemand::field>(
std::forward<fallback::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<fallback::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<fallback::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string> simdjson_result<fallback::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<fallback::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<fallback::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<fallback::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for fallback */
/* including simdjson/generic/ondemand/json_iterator-inl.h for fallback: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::json_iterator>::simdjson_result(fallback::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<fallback::ondemand::json_iterator>(std::forward<fallback::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<fallback::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<fallback::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for fallback */
/* including simdjson/generic/ondemand/json_type-inl.h for fallback: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::json_type>::simdjson_result(fallback::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<fallback::ondemand::json_type>(std::forward<fallback::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<fallback::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<fallback::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for fallback */
/* including simdjson/generic/ondemand/logger-inl.h for fallback: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace fallback {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for fallback */
/* including simdjson/generic/ondemand/object-inl.h for fallback: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::object>::simdjson_result(fallback::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<fallback::ondemand::object>(std::forward<fallback::ondemand::object>(value)) {}
simdjson_inline simdjson_result<fallback::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<fallback::ondemand::object>(error) {}
simdjson_inline simdjson_result<fallback::ondemand::object_iterator> simdjson_result<fallback::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<fallback::ondemand::object_iterator> simdjson_result<fallback::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<fallback::ondemand::object_iterator> simdjson_result<fallback::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<fallback::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<fallback::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<fallback::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<fallback::ondemand::value> simdjson_result<fallback::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, fallback::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<fallback::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires fallback::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, fallback::ondemand::value>
simdjson_inline fallback::ondemand::for_each_result
simdjson_result<fallback::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, fallback::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires fallback::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(fallback::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline fallback::ondemand::for_each_result
simdjson_result<fallback::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(fallback::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(fallback::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline fallback::ondemand::for_each_result
simdjson_result<fallback::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(fallback::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<fallback::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<fallback::ondemand::object_position> simdjson_result<fallback::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<fallback::ondemand::object>::revert_position(fallback::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<fallback::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<fallback::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<fallback::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for fallback */
/* including simdjson/generic/ondemand/object_iterator-inl.h for fallback: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::object_iterator>::simdjson_result(
fallback::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<fallback::ondemand::object_iterator>(std::forward<fallback::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<fallback::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<fallback::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<fallback::ondemand::field> simdjson_result<fallback::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<fallback::ondemand::object_iterator>::operator==(const simdjson_result<fallback::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<fallback::ondemand::object_iterator>::operator!=(const simdjson_result<fallback::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<fallback::ondemand::object_iterator> &simdjson_result<fallback::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for fallback */
/* including simdjson/generic/ondemand/ranges-inl.h for fallback: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace fallback {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::fallback::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::fallback::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::fallback::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::fallback::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::fallback::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::fallback::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for fallback */
/* including simdjson/generic/ondemand/parser-inl.h for fallback: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::parser>::simdjson_result(fallback::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<fallback::ondemand::parser>(std::forward<fallback::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<fallback::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<fallback::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for fallback */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for fallback: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string>::simdjson_result(fallback::ondemand::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<fallback::ondemand::raw_json_string>(std::forward<fallback::ondemand::raw_json_string>(value)) {}
simdjson_inline simdjson_result<fallback::ondemand::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<fallback::ondemand::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<fallback::ondemand::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline char simdjson_result<fallback::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
if (error()) { return error(); }
return first[i];
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<fallback::ondemand::raw_json_string>::unescape(fallback::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<fallback::ondemand::raw_json_string>::unescape_wobbly(fallback::ondemand::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* end file simdjson/generic/ondemand/raw_json_string-inl.h for fallback */
/* including simdjson/generic/ondemand/token_iterator-inl.h for fallback: #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/token_iterator-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::token_iterator>::simdjson_result(fallback::ondemand::token_iterator &&value) noexcept
: implementation_simdjson_result_base<fallback::ondemand::token_iterator>(std::forward<fallback::ondemand::token_iterator>(value)) {}
simdjson_inline simdjson_result<fallback::ondemand::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<fallback::ondemand::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/token_iterator-inl.h for fallback */
/* including simdjson/generic/ondemand/value_iterator-inl.h for fallback: #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/value_iterator-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace fallback {
namespace ondemand {
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
bool escaping{false};
for (uint32_t i = 1; i < max_len; i++) {
switch (json[i]) {
case '"':
if (!escaping) { return true; }
escaping = false;
break;
case '\\':
escaping = !escaping;
break;
default:
escaping = false;
break;
}
}
return false;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
SIMDJSON_TRY(end_container());
return false;
}
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
// call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != '}') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
// If the last character is } *and* the first gibberish character is also '}'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed object.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
auto error = check_root_object();
if(error) { return error; }
return started_object();
}
simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
#if SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
return false;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return false;
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
// If someone queried a key but they not did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
//
// ```
// // At the beginning of a field
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // At the end of the object
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// ```
//
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
SIMDJSON_TRY(reset_object().get(has_value));
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
return raw_json_string(key);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
assert_at_next();
const uint8_t *k = _json_iter->return_current_and_advance();
if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
// After return_current_and_advance(), the current token is the ':' that follows
// the key. The closing quote sits just before it (only JSON whitespace may
// intervene), so step back from the ':' to the closing quote to get the length.
// In minified JSON this is a single back-step.
const char *q = reinterpret_cast<const char *>(_json_iter->peek());
do { --q; } while (*q != '"');
key = raw_json_string(k);
len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
assert_at_next();
if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
answer += std::string(" ]");
return answer;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
SIMDJSON_TRY( end_container() );
return false;
}
_json_iter->descend_to(depth()+1);
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
// also call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != ']') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
// If the last character is ] *and* the first gibberish character is also ']'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed array.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
auto error = check_root_array();
if (error) { return error; }
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
}
simdjson_inline bool value_iterator::is_root_negative() noexcept {
return numberparsing::is_negative(peek_root_scalar("numbersign"));
}
simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
return numberparsing::is_integer(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
return numberparsing::get_number_type(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
number num;
error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
if(error) { return error; }
return num;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<fallback::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_inline value_iterator value_iterator::child() const noexcept {
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool value_iterator::at_end() const noexcept {
return _json_iter->at_end();
}
simdjson_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.position() == start_position();
}
simdjson_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
return _json_iter->error;
}
simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
return _json_iter->string_buf_loc();
}
simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
return *_json_iter;
}
simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
return *_json_iter;
}
simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(start_position());
}
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
logger::log_start_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
const uint8_t *json;
if (!is_at_start()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
return SUCCESS;
}
simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
return _json_iter->peek();
}
simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
logger::log_error(*_json_iter, start_position(), depth(), message);
return INCORRECT_TYPE;
}
simdjson_inline bool value_iterator::is_at_start() const noexcept {
return position() == start_position();
}
simdjson_inline bool value_iterator::is_at_key() const noexcept {
// Keys are at the same depth as the object.
// Note here that we could be safer and check that we are within an object,
// but we do not.
//
// As long as we are at the object's depth, in a valid document,
// we will only ever be at { , : or the actual string key: ".
return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
}
simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = position() - start_position();
return delta == 1 || delta == 2;
}
inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_inline void value_iterator::move_at_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position);
}
simdjson_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
// Unlike reenter_child(), this does not require the live depth to be
// exactly one level shallower than depth, nor does it validate against
// the parser's per-depth container-start bookkeeping: neither holds in
// general for a caller-supplied snapshot (see object_position). What
// must still always hold, regardless of what was captured or how far
// the live iterator has since moved, is that position and depth are
// themselves sane values -- this is the same bound reenter_child()
// itself applies unconditionally.
SIMDJSON_ASSUME(position != nullptr);
SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
_json_iter->_depth = depth;
_json_iter->token.set_position(position);
}
simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_array();
}
simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_object();
}
inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return json_type::unknown;
}
}
simdjson_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<fallback::ondemand::value_iterator>::simdjson_result(fallback::ondemand::value_iterator &&value) noexcept
: implementation_simdjson_result_base<fallback::ondemand::value_iterator>(std::forward<fallback::ondemand::value_iterator>(value)) {}
simdjson_inline simdjson_result<fallback::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<fallback::ondemand::value_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/value_iterator-inl.h for fallback */
/* including simdjson/generic/ondemand/serialization-inl.h for fallback: #include "simdjson/generic/ondemand/serialization-inl.h" */
/* begin file simdjson/generic/ondemand/serialization-inl.h for fallback */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-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/serialization.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace fallback::ondemand;
fallback::ondemand::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
fallback::ondemand::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
fallback::ondemand::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(fallback::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<fallback::ondemand::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace fallback { namespace ondemand {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::fallback::ondemand::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::fallback::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::fallback::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* end file simdjson/generic/ondemand/serialization-inl.h for fallback */
// JSON path accessor (compile-time) - must be after inline definitions
/* including simdjson/generic/ondemand/compile_time_accessors.h for fallback: #include "simdjson/generic/ondemand/compile_time_accessors.h" */
/* begin file simdjson/generic/ondemand/compile_time_accessors.h for fallback */
/**
* Compile-time JSON Path and JSON Pointer accessors using C++26 reflection (P2996)
*
* This file validates JSON paths/pointers against struct definitions at compile time
* and generates optimized accessor code with zero runtime overhead.
*
* ## How It Works
*
* **Compile Time**: Path is parsed, validated against struct, types are checked
* **Runtime**: Direct navigation with no parsing or validation overhead
*
* Example:
* ```cpp
* struct User { std::string name; std::vector<std::string> emails; };
*
* std::string email;
* path_accessor<User, ".emails[0]">::extract_field(doc, email);
*
* // Compile time validates:
* // 1. User has "emails" field
* // 2. "emails" is array-like
* // 3. Element type is std::string
* // 4. static_assert(^^std::string == ^^std::string)
*
* // Runtime just navigates:
* // doc.get_object().find_field("emails").get_array().at(0).get(email)
* ```
*
* ## Key Reflection APIs
*
* - `^^Type`: Reflect operator, converts type to std::meta::info
* - `std::meta::nonstatic_data_members_of(type)`: Get all fields of a struct
* - `std::meta::identifier_of(member)`: Get field name as string_view
* - `std::meta::type_of(member)`: Get reflected type of a field
* - `std::meta::is_array_type(type)`: Check if C-style array
* - `std::meta::remove_extent(array)`: Extract element type from array
* - `std::meta::members_of(type)`: Get all members including typedefs
* - `std::meta::is_type(member)`: Check if member is a type (vs field)
*
* All operations execute at compile time in consteval contexts.
*/
#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H */
/* amalgamation skipped (editor-only): */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
// is unlikely that we will have reflection support without concepts support.
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#include <string_view>
#include <cstddef>
#include <array>
namespace simdjson {
namespace fallback {
namespace ondemand {
/***
* JSONPath implementation for compile-time access
* RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
*/
namespace json_path {
// Note: value type must be fully defined before this header is included
// This is ensured by including this in amalgamated.h after value-inl.h
using ::simdjson::fallback::ondemand::value;
// Path step types
enum class step_type {
field, // .field_name or ["field_name"]
array_index // [index]
};
// Represents a single step in a JSON path
template<std::size_t N>
struct path_step {
step_type type;
char key[N]; // Field name (empty for array indices)
std::size_t index; // Array index (0 for field access)
constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
: type(t), index(idx) {
for (std::size_t i = 0; i < N; ++i) {
key[i] = k[i];
}
}
constexpr std::string_view key_view() const {
return {key, N - 1};
}
};
// Helper to create field step
template<std::size_t N>
consteval auto make_field_step(const char (&name)[N]) {
return path_step<N>(step_type::field, name, 0);
}
// Helper to create array index step
consteval auto make_index_step(std::size_t idx) {
return path_step<1>(step_type::array_index, "", idx);
}
// Parse state for compile-time JSON path parsing
struct parse_result {
bool success;
std::size_t pos;
std::string_view error_msg;
};
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
struct json_path_parser {
static constexpr std::string_view path_str = Path.view();
// Skip leading $ if present
static consteval std::size_t skip_root() {
if (!path_str.empty() && path_str[0] == '$') {
return 1;
}
return 0;
}
// Count the number of steps in the path at compile time
static consteval std::size_t count_steps() {
std::size_t count = 0;
std::size_t i = skip_root();
while (i < path_str.size()) {
if (path_str[i] == '.') {
// Field access: .field
++i;
if (i >= path_str.size()) break;
// Skip field name
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
++i;
}
++count;
} else if (path_str[i] == '[') {
// Array or bracket notation
++i;
if (i >= path_str.size()) break;
if (path_str[i] == '"' || path_str[i] == '\'') {
// Field access: ["field"] or ['field']
char quote = path_str[i];
++i;
while (i < path_str.size() && path_str[i] != quote) {
++i;
}
if (i < path_str.size()) ++i; // skip closing quote
if (i < path_str.size() && path_str[i] == ']') ++i;
} else {
// Array index: [0], [123]
while (i < path_str.size() && path_str[i] != ']') {
++i;
}
if (i < path_str.size()) ++i; // skip ]
}
++count;
} else {
++i;
}
}
return count;
}
// Parse a field name at compile time
static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
std::size_t len = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
out[len++] = path_str[i++];
}
out[len] = '\0';
return i;
}
// Parse an array index at compile time
static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
std::size_t index = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
index = index * 10 + (path_str[i] - '0');
++i;
}
return {i, index};
}
};
// Compile-time path accessor generator
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
using value = ::simdjson::fallback::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
// If T is a struct, validates the path at compile time
// If T is void, skips validation
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
// Validate path at compile time if T is a struct
if constexpr (std::is_class_v<T>) {
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
}
// Parse the path at compile time to build access steps
return access_impl<parser.skip_root()>(doc_or_val.get_value());
}
// Extract value at path directly into target with compile-time type validation
// Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
// Validate path exists in struct definition
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
// Get the type at the end of the path
constexpr auto final_type = get_final_type();
// Verify target type matches the field type
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
// All validation done at compile time - just navigate and extract
auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get the final type by walking the path through the struct type
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
// .field syntax
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) break;
if (path_view[i] == '"' || path_view[i] == '\'') {
// ["field"] syntax
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip quote
if (i < path_view.size() && path_view[i] == ']') ++i;
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else {
// [index] syntax - extract element type
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
current_type = get_element_type_reflected(current_type);
}
} else {
++i;
}
}
return current_type;
}
private:
// Walk path and extract directly into final field using compile-time reflection
template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
if (current.error()) return current.error();
// Base case: end of path - extract into target
if constexpr (PathPos >= path_view.size()) {
return current.get(target_ref);
}
// Field access: .field_name
else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
}
// Bracket notation: [index] or ["field"]
else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
constexpr auto elem_type = get_element_type_reflected(CurrentType);
static_assert(elem_type != ^^void, "Could not determine array element type");
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto elem_value = arr.at(index);
if constexpr (next_pos >= path_view.size()) {
return elem_value.get(target_ref);
} else {
return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
}
}
}
// Skip unexpected characters and continue
else {
return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
}
}
// Find member by name in reflected type
static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == name) {
return mem;
}
}
}
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if (current.error()) return current;
if constexpr (PathPos >= path_view.size()) {
return current;
} else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto next_value = arr.at(index);
return access_impl<next_pos>(next_value);
}
} else {
return access_impl<PathPos + 1>(current);
}
}
// Parse next field name
static consteval auto parse_next_field(std::size_t start) {
std::size_t i = start + 1;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
return std::make_tuple(field_name, i);
}
// Parse bracket notation: returns (is_field, field_name, next_pos, index)
static consteval auto parse_bracket(std::size_t start) {
std::size_t i = start + 1; // skip '['
if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
// Field access
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip closing quote
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(true, field_name, i, std::size_t(0));
} else {
// Array index
std::size_t index = 0;
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
index = index * 10 + (path_view[i] - '0');
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(false, std::string_view{}, i, index);
}
}
public:
// Check if reflected type is array-like (C-style array or indexable container)
// Uses reflection to test: 1) std::meta::is_array_type() for C arrays
// 2) std::meta::substitute() to test concepts::indexable_container concept
static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return true;
}
if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
}
return false;
}
// Extract element type from reflected array or container
// For C arrays: uses std::meta::remove_extent()
// For containers: finds value_type member using std::meta::members_of()
static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return std::meta::remove_extent(type_reflection);
}
auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::is_type(mem)) {
auto name = std::meta::identifier_of(mem);
if (name == "value_type") {
return mem;
}
}
}
return ^^void;
}
private:
// Check if type has member with given name
template<typename Type>
static consteval bool has_member(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return true;
}
}
return false;
}
// Get type of member by name
template<typename Type>
static consteval auto get_member_type(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return std::meta::type_of(mem);
}
}
return ^^void;
}
// Check if non-reflected type is array-like
template<typename Type>
static consteval bool is_container_type() {
using BaseType = std::remove_cvref_t<Type>;
if constexpr (requires { typename BaseType::value_type; }) {
return true;
}
if constexpr (std::is_array_v<BaseType>) {
return true;
}
return false;
}
// Extract element type from non-reflected container
template<typename Type>
using extract_element_type = std::conditional_t<
requires { typename std::remove_cvref_t<Type>::value_type; },
typename std::remove_cvref_t<Type>::value_type,
std::conditional_t<
std::is_array_v<std::remove_cvref_t<Type>>,
std::remove_extent_t<std::remove_cvref_t<Type>>,
void
>
>;
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) return false;
if (path_view[i] == '"' || path_view[i] == '\'') {
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i;
if (i < path_view.size() && path_view[i] == ']') ++i;
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else {
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
if (!is_array_like_reflected(current_type)) {
return false;
}
auto new_type = get_element_type_reflected(current_type);
if (new_type == ^^void) {
return false;
}
current_type = new_type;
}
} else {
++i;
}
}
return true;
}
};
// Compile-time path accessor with validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::fallback::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<T, Path>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::fallback::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<void, Path>;
return accessor::access(doc_or_val);
}
// ============================================================================
// JSON Pointer Compile-Time Support (RFC 6901)
// ============================================================================
// JSON Pointer parser: /field/0/nested (slash-separated)
template<constevalutil::fixed_string Pointer>
struct json_pointer_parser {
static constexpr std::string_view pointer_str = Pointer.view();
// Unescape token: ~0 -> ~, ~1 -> /
static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
out_len = 0;
for (std::size_t i = 0; i < src.size(); ++i) {
if (src[i] == '~' && i + 1 < src.size()) {
if (src[i + 1] == '0') {
dest[out_len++] = '~';
++i;
} else if (src[i + 1] == '1') {
dest[out_len++] = '/';
++i;
} else {
dest[out_len++] = src[i];
}
} else {
dest[out_len++] = src[i];
}
}
}
// Check if token is numeric
static consteval bool is_numeric(std::string_view token) {
if (token.empty()) return false;
if (token[0] == '0' && token.size() > 1) return false;
for (char c : token) {
if (c < '0' || c > '9') return false;
}
return true;
}
// Parse numeric token to index
static consteval std::size_t parse_index(std::string_view token) {
std::size_t result = 0;
for (char c : token) {
result = result * 10 + (c - '0');
}
return result;
}
// Count tokens in pointer
static consteval std::size_t count_tokens() {
if (pointer_str.empty() || pointer_str == "/") return 0;
std::size_t count = 0;
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
while (pos < pointer_str.size()) {
++count;
std::size_t next_slash = pointer_str.find('/', pos);
if (next_slash == std::string_view::npos) break;
pos = next_slash + 1;
}
return count;
}
// Get Nth token
static consteval std::string_view get_token(std::size_t token_index) {
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
std::size_t current_token = 0;
while (current_token < token_index) {
std::size_t next_slash = pointer_str.find('/', pos);
pos = next_slash + 1;
++current_token;
}
std::size_t token_end = pointer_str.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_str.size();
return pointer_str.substr(pos, token_end - pos);
}
};
// JSON Pointer accessor
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
using parser = json_pointer_parser<Pointer>;
static constexpr std::string_view pointer_view = Pointer.view();
static constexpr std::size_t token_count = parser::count_tokens();
// Validate pointer against struct definition
static consteval bool validate_pointer() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
// Extract token up to next /
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
return false;
}
current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
} else {
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) return false;
}
pos = token_end + 1;
}
return true;
}
// Recursive accessor
template<std::size_t TokenIndex>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if constexpr (TokenIndex >= token_count) {
return current;
} else {
constexpr std::string_view token = parser::get_token(TokenIndex);
if constexpr (parser::is_numeric(token)) {
constexpr std::size_t index = parser::parse_index(token);
auto arr = current.get_array().value_unsafe();
auto next_value = arr.at(index);
return access_impl<TokenIndex + 1>(next_value);
} else {
auto obj = current.get_object().value_unsafe();
auto next_value = obj.find_field_unordered(token);
return access_impl<TokenIndex + 1>(next_value);
}
}
}
// Access JSON value at pointer
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
if constexpr (std::is_class_v<T>) {
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
}
if (pointer_view.empty() || pointer_view == "/") {
if constexpr (requires { doc_or_val.get_value(); }) {
return doc_or_val.get_value();
} else {
return doc_or_val;
}
}
simdjson_result<value> current = doc_or_val.get_value();
return access_impl<0>(current);
}
// Extract value at pointer directly into target with type validation
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
constexpr auto final_type = get_final_type();
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
simdjson_result<value> current_value = doc_or_val.get_value();
auto json_value = access_impl<0>(current_value);
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get final type by walking pointer through struct
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
} else {
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
break;
}
}
}
pos = token_end + 1;
}
return current_type;
}
};
// Compile-time JSON Pointer accessor with validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::fallback::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<T, Pointer>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::fallback::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<void, Pointer>;
return accessor::access(doc_or_val);
}
} // namespace json_path
} // namespace ondemand
} // namespace fallback
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* end file simdjson/generic/ondemand/compile_time_accessors.h for fallback */
/* end file simdjson/generic/ondemand/amalgamated.h for fallback */
/* including simdjson/fallback/end.h: #include "simdjson/fallback/end.h" */
/* begin file simdjson/fallback/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/fallback/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/* undefining SIMDJSON_IMPLEMENTATION from "fallback" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/fallback/end.h */
#endif // SIMDJSON_FALLBACK_ONDEMAND_H
/* end file simdjson/fallback/ondemand.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(haswell)
/* including simdjson/haswell/ondemand.h: #include "simdjson/haswell/ondemand.h" */
/* begin file simdjson/haswell/ondemand.h */
#ifndef SIMDJSON_HASWELL_ONDEMAND_H
#define SIMDJSON_HASWELL_ONDEMAND_H
/* including simdjson/haswell/begin.h: #include "simdjson/haswell/begin.h" */
/* begin file simdjson/haswell/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "haswell" */
#define SIMDJSON_IMPLEMENTATION haswell
/* including simdjson/haswell/base.h: #include "simdjson/haswell/base.h" */
/* begin file simdjson/haswell/base.h */
#ifndef SIMDJSON_HASWELL_BASE_H
#define SIMDJSON_HASWELL_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL
namespace simdjson {
/**
* Implementation for Haswell (Intel AVX2).
*/
namespace haswell {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BASE_H
/* end file simdjson/haswell/base.h */
/* including simdjson/haswell/intrinsics.h: #include "simdjson/haswell/intrinsics.h" */
/* begin file simdjson/haswell/intrinsics.h */
#ifndef SIMDJSON_HASWELL_INTRINSICS_H
#define SIMDJSON_HASWELL_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
* e.g., if __AVX2__ is set... in turn, we normally set these
* macros by compiling against the corresponding architecture
* (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
* software with these advanced instructions. In simdjson, we
* want to compile the whole program for a generic target,
* and only target our specific kernels. As a workaround,
* we directly include the needed headers. These headers would
* normally guard against such usage, but we carefully included
* <x86intrin.h> (or <intrin.h>) before, so the headers
* are fooled.
*/
#include <bmiintrin.h> // for _blsr_u64
#include <lzcntintrin.h> // for __lzcnt64
#include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
#include <smmintrin.h>
#include <tmmintrin.h>
#include <avxintrin.h>
#include <avx2intrin.h>
#include <wmmintrin.h> // for _mm_clmulepi64_si128
// unfortunately, we may not get _blsr_u64, but, thankfully, clang
// has it as a macro.
#ifndef _blsr_u64
// we roll our own
#define _blsr_u64(n) ((n - 1) & n)
#endif // _blsr_u64
#endif // SIMDJSON_CLANG_VISUAL_STUDIO
static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for haswell kernel.");
#endif // SIMDJSON_HASWELL_INTRINSICS_H
/* end file simdjson/haswell/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
// We enable bmi2 only if LLVM/clang is used, because GCC may not
// make good use of it. See https://github.com/simdjson/simdjson/pull/2243
#if defined(__clang__)
SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt")
#else
SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt")
#endif
#endif
/* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */
/* begin file simdjson/haswell/bitmanipulation.h */
#ifndef SIMDJSON_HASWELL_BITMANIPULATION_H
#define SIMDJSON_HASWELL_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BITMANIPULATION_H
/* end file simdjson/haswell/bitmanipulation.h */
/* including simdjson/haswell/bitmask.h: #include "simdjson/haswell/bitmask.h" */
/* begin file simdjson/haswell/bitmask.h */
#ifndef SIMDJSON_HASWELL_BITMASK_H
#define SIMDJSON_HASWELL_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processor supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_BITMASK_H
/* end file simdjson/haswell/bitmask.h */
/* including simdjson/haswell/numberparsing_defs.h: #include "simdjson/haswell/numberparsing_defs.h" */
/* begin file simdjson/haswell/numberparsing_defs.h */
#ifndef SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
#define SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace numberparsing {
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace haswell
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_HASWELL_NUMBERPARSING_DEFS_H
/* end file simdjson/haswell/numberparsing_defs.h */
/* including simdjson/haswell/simd.h: #include "simdjson/haswell/simd.h" */
/* begin file simdjson/haswell/simd.h */
#ifndef SIMDJSON_HASWELL_SIMD_H
#define SIMDJSON_HASWELL_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m256i value;
// Zero constructor
simdjson_inline base() : value{__m256i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m256i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m256i&() const { return this->value; }
simdjson_inline operator __m256i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm256_or_si256(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm256_and_si256(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm256_xor_si256(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm256_andnot_si256(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint32_t bitmask_t;
typedef uint64_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm256_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm256_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm256_testz_si256(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm256_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm256_setzero_si256(); }
static simdjson_inline simd8<T> load(const T values[32]) {
return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm256_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm256_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm256_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint32_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in four steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second least significant 8 bits
uint8_t mask3 = uint8_t(mask >> 16); // ...
uint8_t mask4 = uint8_t(mask >> 24); // ...
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m256i shufmask = _mm256_set_epi64x(thintable_epi8[mask4], thintable_epi8[mask3],
thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask and so forth
shufmask =
_mm256_add_epi8(shufmask, _mm256_set_epi32(0x18181818, 0x18181818,
0x10101010, 0x10101010, 0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m256i pruned = _mm256_shuffle_epi8(*this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
int pop3 = BitsSetTable256mul2[mask3];
// then load the corresponding mask
// could be done with _mm256_loadu2_m128i but many standard libraries omit this intrinsic.
__m256i v256 = _mm256_castsi128_si256(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8)));
__m256i compactmask = _mm256_insertf128_si256(v256,
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop3 * 8)), 1);
__m256i almostthere = _mm256_shuffle_epi8(pruned, compactmask);
// We just need to write out the result.
// This is the tricky bit that is hard to do
// if we want to return a SIMD register, since there
// is no single-instruction approach to recombine
// the two 128-bit lanes with an offset.
__m128i v128;
v128 = _mm256_castsi256_si128(almostthere);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output), v128);
v128 = _mm256_extractf128_si256(almostthere, 1);
_mm_storeu_si128( reinterpret_cast<__m128i *>(output + 16 - count_ones(mask & 0xFFFF)), v128);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31
) : simd8(_mm256_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm256_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm256_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm256_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm256_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31
) : simd8(_mm256_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm256_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm256_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm256_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm256_min_epu8(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm256_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm256_testz_si256(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm256_testz_si256(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm256_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+32)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint32_t mask1 = uint32_t(mask);
uint32_t mask2 = uint32_t(mask >> 32);
this->chunks[0].compress(mask1, output);
this->chunks[1].compress(mask2, output + 32 - count_ones(mask1));
return 64 - count_ones(mask);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
uint64_t r_hi = this->chunks[1].to_bitmask();
return r_lo | (r_hi << 32);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0] | this->chunks[1];
}
simdjson_inline simd8x64<T> bit_or(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<T>(
this->chunks[0] | mask,
this->chunks[1] | mask
);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_SIMD_H
/* end file simdjson/haswell/simd.h */
/* including simdjson/haswell/stringparsing_defs.h: #include "simdjson/haswell/stringparsing_defs.h" */
/* begin file simdjson/haswell/stringparsing_defs.h */
#ifndef SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
#define SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 15 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
// store to dest unconditionally - we can overwrite the bits we don't like later
v.store(dst);
return {
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_HASWELL_STRINGPARSING_DEFS_H
/* end file simdjson/haswell/stringparsing_defs.h */
/* end file simdjson/haswell/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for haswell: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for haswell */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for haswell: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::haswell::number_type */
using number_type = simdjson::haswell::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for haswell */
/* including simdjson/generic/ondemand/deserialize.h for haswell: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for haswell */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<haswell::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<haswell::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<haswell::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<haswell::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = haswell::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = haswell::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = haswell::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = haswell::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = haswell::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = haswell::ondemand::array;
using object_type = haswell::ondemand::object;
using value_type = haswell::ondemand::value;
using document_type = haswell::ondemand::document;
using document_reference_type = haswell::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for haswell */
/* including simdjson/generic/ondemand/value_iterator.h for haswell: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::value_iterator> : public haswell::implementation_simdjson_result_base<haswell::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(haswell::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for haswell */
/* including simdjson/generic/ondemand/value.h for haswell: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::value> : public haswell::implementation_simdjson_result_base<haswell::ondemand::value> {
public:
simdjson_inline simdjson_result(haswell::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<haswell::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator haswell::ondemand::array() noexcept(false);
simdjson_inline operator haswell::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator haswell::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<haswell::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<haswell::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<haswell::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<haswell::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<haswell::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<haswell::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<haswell::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<haswell::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<haswell::ondemand::value>::get<haswell::ondemand::value>(
haswell::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<haswell::ondemand::value>
simdjson_result<haswell::ondemand::value>::get<haswell::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for haswell */
/* including simdjson/generic/ondemand/logger.h for haswell: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for haswell */
/* including simdjson/generic/ondemand/token_iterator.h for haswell: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::token_iterator> : public haswell::implementation_simdjson_result_base<haswell::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(haswell::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for haswell */
/* including simdjson/generic/ondemand/json_iterator.h for haswell: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::json_iterator> : public haswell::implementation_simdjson_result_base<haswell::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(haswell::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for haswell */
/* including simdjson/generic/ondemand/json_type.h for haswell: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::json_type> : public haswell::implementation_simdjson_result_base<haswell::ondemand::json_type> {
public:
simdjson_inline simdjson_result(haswell::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for haswell */
/* including simdjson/generic/ondemand/raw_json_string.h for haswell: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::raw_json_string> : public haswell::implementation_simdjson_result_base<haswell::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(haswell::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(haswell::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(haswell::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for haswell */
/* including simdjson/generic/ondemand/parser.h for haswell: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::parser> : public haswell::implementation_simdjson_result_base<haswell::ondemand::parser> {
public:
simdjson_inline simdjson_result(haswell::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for haswell */
// All other declarations
/* including simdjson/generic/ondemand/array.h for haswell: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::array> : public haswell::implementation_simdjson_result_base<haswell::ondemand::array> {
public:
simdjson_inline simdjson_result(haswell::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, haswell::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, haswell::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, haswell::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, haswell::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for haswell */
/* including simdjson/generic/ondemand/array_iterator.h for haswell: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::array_iterator> : public haswell::implementation_simdjson_result_base<haswell::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<haswell::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(haswell::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<haswell::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<haswell::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<haswell::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for haswell */
/* including simdjson/generic/ondemand/document.h for haswell: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::document> : public haswell::implementation_simdjson_result_base<haswell::ondemand::document> {
public:
simdjson_inline simdjson_result(haswell::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using haswell::implementation_simdjson_result_base<haswell::ondemand::document>::operator*;
using haswell::implementation_simdjson_result_base<haswell::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, haswell::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator haswell::ondemand::array() & noexcept(false);
simdjson_inline operator haswell::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator haswell::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator haswell::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<haswell::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<haswell::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::document_reference> : public haswell::implementation_simdjson_result_base<haswell::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(haswell::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator haswell::ondemand::array() & noexcept(false);
simdjson_inline operator haswell::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator haswell::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator haswell::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<haswell::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<haswell::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for haswell */
/* including simdjson/generic/ondemand/document_stream.h for haswell: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace haswell {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::document_stream> : public haswell::implementation_simdjson_result_base<haswell::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(haswell::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for haswell */
/* including simdjson/generic/ondemand/field.h for haswell: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::field> : public haswell::implementation_simdjson_result_base<haswell::ondemand::field> {
public:
simdjson_inline simdjson_result(haswell::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<haswell::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for haswell */
/* including simdjson/generic/ondemand/key_selector.h for haswell: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace haswell {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for haswell */
/* including simdjson/generic/ondemand/object.h for haswell: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::object> : public haswell::implementation_simdjson_result_base<haswell::ondemand::object> {
public:
simdjson_inline simdjson_result(haswell::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<haswell::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<haswell::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<haswell::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<haswell::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(haswell::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, haswell::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, haswell::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, haswell::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, haswell::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires haswell::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, haswell::ondemand::value>
simdjson_inline haswell::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, haswell::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires haswell::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(haswell::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline haswell::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(haswell::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(haswell::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline haswell::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(haswell::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for haswell */
/* including simdjson/generic/ondemand/object_iterator.h for haswell: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<haswell::ondemand::object_iterator> : public haswell::implementation_simdjson_result_base<haswell::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(haswell::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<haswell::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<haswell::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<haswell::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<haswell::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for haswell */
/* including simdjson/generic/ondemand/ranges.h for haswell: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::haswell::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::haswell::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for haswell */
/* including simdjson/generic/ondemand/serialization.h for haswell: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, haswell::ondemand::document> &&
!std::same_as<T, haswell::ondemand::value> &&
!std::same_as<T, haswell::ondemand::object> &&
!std::same_as<T, haswell::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace haswell { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::object> x);
#endif
}}} // namespace simdjson::haswell::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for haswell */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for haswell: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for haswell */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
haswell::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, haswell::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
haswell::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, haswell::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
haswell::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, haswell::ondemand::value &val, T &out) noexcept(false) {
haswell::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, haswell::ondemand::document &doc, T &out) noexcept(false) {
haswell::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, haswell::ondemand::document_reference &doc, T &out) noexcept(false) {
haswell::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, haswell::ondemand::number>
&& !std::is_same_v<T, haswell::ondemand::document>
&& !std::is_same_v<T, haswell::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^haswell::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = haswell::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, haswell::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
haswell::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
haswell::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
haswell::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
haswell::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, haswell::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
haswell::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, haswell::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, haswell::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, haswell::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for haswell */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for haswell: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::array>::simdjson_result(
haswell::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<haswell::ondemand::array>(
std::forward<haswell::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<haswell::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<haswell::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<haswell::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<haswell::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for haswell */
/* including simdjson/generic/ondemand/array_iterator-inl.h for haswell: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::array_iterator>::simdjson_result(
haswell::ondemand::array_iterator &&value
) noexcept
: haswell::implementation_simdjson_result_base<haswell::ondemand::array_iterator>(std::forward<haswell::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: haswell::implementation_simdjson_result_base<haswell::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<haswell::ondemand::array_iterator>::operator==(const simdjson_result<haswell::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<haswell::ondemand::array_iterator>::operator!=(const simdjson_result<haswell::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> &simdjson_result<haswell::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<haswell::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for haswell */
/* including simdjson/generic/ondemand/value-inl.h for haswell: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace haswell {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::value>::simdjson_result(
haswell::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<haswell::ondemand::value>(
std::forward<haswell::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<haswell::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<haswell::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<haswell::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<haswell::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<haswell::ondemand::array> simdjson_result<haswell::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<haswell::ondemand::object> simdjson_result<haswell::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<haswell::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<haswell::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<haswell::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<haswell::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<haswell::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<haswell::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<haswell::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<haswell::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<haswell::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<haswell::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<haswell::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<haswell::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<haswell::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<haswell::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<haswell::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<haswell::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<haswell::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<haswell::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> simdjson_result<haswell::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<haswell::ondemand::value>::get<haswell::ondemand::value>(haswell::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<haswell::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<haswell::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::get<haswell::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<haswell::ondemand::json_type> simdjson_result<haswell::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<haswell::number_type> simdjson_result<haswell::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<haswell::ondemand::number> simdjson_result<haswell::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<haswell::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<haswell::ondemand::value>::operator haswell::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::value>::operator haswell::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::value>::operator haswell::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<haswell::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<haswell::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<haswell::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for haswell */
/* including simdjson/generic/ondemand/document-inl.h for haswell: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace haswell {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::document>::simdjson_result(
haswell::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<haswell::ondemand::document>(
std::forward<haswell::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<haswell::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<haswell::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<haswell::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<haswell::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<haswell::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<haswell::ondemand::array> simdjson_result<haswell::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<haswell::ondemand::object> simdjson_result<haswell::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<haswell::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<haswell::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<haswell::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<haswell::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<haswell::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<haswell::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<haswell::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<haswell::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<haswell::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<haswell::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<haswell::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<haswell::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<haswell::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<haswell::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<haswell::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<haswell::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<haswell::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> simdjson_result<haswell::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<haswell::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<haswell::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<haswell::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<haswell::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<haswell::ondemand::document> simdjson_result<haswell::ondemand::document>::get<haswell::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<haswell::ondemand::document> simdjson_result<haswell::ondemand::document>::get<haswell::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<haswell::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document>::get<haswell::ondemand::document>(haswell::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document>::get<haswell::ondemand::document>(haswell::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<haswell::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<haswell::ondemand::json_type> simdjson_result<haswell::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<haswell::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<haswell::number_type> simdjson_result<haswell::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<haswell::ondemand::number> simdjson_result<haswell::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, haswell::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<haswell::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator haswell::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator haswell::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator haswell::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<haswell::ondemand::document>::operator haswell::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<haswell::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<haswell::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<haswell::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<haswell::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace haswell {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::simdjson_result(haswell::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<haswell::ondemand::document_reference>(std::forward<haswell::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<haswell::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<haswell::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<haswell::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<haswell::ondemand::array_iterator> simdjson_result<haswell::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<haswell::ondemand::array> simdjson_result<haswell::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<haswell::ondemand::object> simdjson_result<haswell::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<haswell::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<haswell::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<haswell::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<haswell::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<haswell::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<haswell::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<haswell::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<haswell::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<haswell::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<haswell::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<haswell::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<haswell::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<haswell::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<haswell::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<haswell::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<haswell::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<haswell::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> simdjson_result<haswell::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<haswell::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<haswell::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<haswell::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, haswell::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<haswell::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<haswell::ondemand::json_type> simdjson_result<haswell::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document_reference>::get(haswell::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document_reference>::get(haswell::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<haswell::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<haswell::number_type> simdjson_result<haswell::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<haswell::ondemand::number> simdjson_result<haswell::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, haswell::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, haswell::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator haswell::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator haswell::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator haswell::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<haswell::ondemand::document_reference>::operator haswell::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<haswell::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<haswell::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for haswell */
/* including simdjson/generic/ondemand/document_stream-inl.h for haswell: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace haswell {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<haswell::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<haswell::ondemand::document_stream>::simdjson_result(
haswell::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<haswell::ondemand::document_stream>(
std::forward<haswell::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for haswell */
/* including simdjson/generic/ondemand/field-inl.h for haswell: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::field>::simdjson_result(
haswell::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<haswell::ondemand::field>(
std::forward<haswell::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<haswell::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<haswell::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string> simdjson_result<haswell::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<haswell::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<haswell::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<haswell::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for haswell */
/* including simdjson/generic/ondemand/json_iterator-inl.h for haswell: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::json_iterator>::simdjson_result(haswell::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<haswell::ondemand::json_iterator>(std::forward<haswell::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<haswell::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<haswell::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for haswell */
/* including simdjson/generic/ondemand/json_type-inl.h for haswell: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::json_type>::simdjson_result(haswell::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<haswell::ondemand::json_type>(std::forward<haswell::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<haswell::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<haswell::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for haswell */
/* including simdjson/generic/ondemand/logger-inl.h for haswell: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace haswell {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for haswell */
/* including simdjson/generic/ondemand/object-inl.h for haswell: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::object>::simdjson_result(haswell::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<haswell::ondemand::object>(std::forward<haswell::ondemand::object>(value)) {}
simdjson_inline simdjson_result<haswell::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<haswell::ondemand::object>(error) {}
simdjson_inline simdjson_result<haswell::ondemand::object_iterator> simdjson_result<haswell::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<haswell::ondemand::object_iterator> simdjson_result<haswell::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<haswell::ondemand::object_iterator> simdjson_result<haswell::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<haswell::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<haswell::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<haswell::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<haswell::ondemand::value> simdjson_result<haswell::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, haswell::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<haswell::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires haswell::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, haswell::ondemand::value>
simdjson_inline haswell::ondemand::for_each_result
simdjson_result<haswell::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, haswell::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires haswell::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(haswell::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline haswell::ondemand::for_each_result
simdjson_result<haswell::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(haswell::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(haswell::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline haswell::ondemand::for_each_result
simdjson_result<haswell::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(haswell::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<haswell::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<haswell::ondemand::object_position> simdjson_result<haswell::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<haswell::ondemand::object>::revert_position(haswell::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<haswell::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<haswell::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<haswell::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for haswell */
/* including simdjson/generic/ondemand/object_iterator-inl.h for haswell: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::object_iterator>::simdjson_result(
haswell::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<haswell::ondemand::object_iterator>(std::forward<haswell::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<haswell::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<haswell::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<haswell::ondemand::field> simdjson_result<haswell::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<haswell::ondemand::object_iterator>::operator==(const simdjson_result<haswell::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<haswell::ondemand::object_iterator>::operator!=(const simdjson_result<haswell::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<haswell::ondemand::object_iterator> &simdjson_result<haswell::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for haswell */
/* including simdjson/generic/ondemand/ranges-inl.h for haswell: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace haswell {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::haswell::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::haswell::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::haswell::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::haswell::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::haswell::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::haswell::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for haswell */
/* including simdjson/generic/ondemand/parser-inl.h for haswell: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::parser>::simdjson_result(haswell::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<haswell::ondemand::parser>(std::forward<haswell::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<haswell::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<haswell::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for haswell */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for haswell: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string>::simdjson_result(haswell::ondemand::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<haswell::ondemand::raw_json_string>(std::forward<haswell::ondemand::raw_json_string>(value)) {}
simdjson_inline simdjson_result<haswell::ondemand::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<haswell::ondemand::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<haswell::ondemand::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline char simdjson_result<haswell::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
if (error()) { return error(); }
return first[i];
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<haswell::ondemand::raw_json_string>::unescape(haswell::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<haswell::ondemand::raw_json_string>::unescape_wobbly(haswell::ondemand::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* end file simdjson/generic/ondemand/raw_json_string-inl.h for haswell */
/* including simdjson/generic/ondemand/token_iterator-inl.h for haswell: #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/token_iterator-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::token_iterator>::simdjson_result(haswell::ondemand::token_iterator &&value) noexcept
: implementation_simdjson_result_base<haswell::ondemand::token_iterator>(std::forward<haswell::ondemand::token_iterator>(value)) {}
simdjson_inline simdjson_result<haswell::ondemand::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<haswell::ondemand::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/token_iterator-inl.h for haswell */
/* including simdjson/generic/ondemand/value_iterator-inl.h for haswell: #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/value_iterator-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace haswell {
namespace ondemand {
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
bool escaping{false};
for (uint32_t i = 1; i < max_len; i++) {
switch (json[i]) {
case '"':
if (!escaping) { return true; }
escaping = false;
break;
case '\\':
escaping = !escaping;
break;
default:
escaping = false;
break;
}
}
return false;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
SIMDJSON_TRY(end_container());
return false;
}
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
// call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != '}') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
// If the last character is } *and* the first gibberish character is also '}'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed object.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
auto error = check_root_object();
if(error) { return error; }
return started_object();
}
simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
#if SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
return false;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return false;
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
// If someone queried a key but they not did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
//
// ```
// // At the beginning of a field
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // At the end of the object
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// ```
//
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
SIMDJSON_TRY(reset_object().get(has_value));
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
return raw_json_string(key);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
assert_at_next();
const uint8_t *k = _json_iter->return_current_and_advance();
if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
// After return_current_and_advance(), the current token is the ':' that follows
// the key. The closing quote sits just before it (only JSON whitespace may
// intervene), so step back from the ':' to the closing quote to get the length.
// In minified JSON this is a single back-step.
const char *q = reinterpret_cast<const char *>(_json_iter->peek());
do { --q; } while (*q != '"');
key = raw_json_string(k);
len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
assert_at_next();
if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
answer += std::string(" ]");
return answer;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
SIMDJSON_TRY( end_container() );
return false;
}
_json_iter->descend_to(depth()+1);
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
// also call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != ']') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
// If the last character is ] *and* the first gibberish character is also ']'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed array.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
auto error = check_root_array();
if (error) { return error; }
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
}
simdjson_inline bool value_iterator::is_root_negative() noexcept {
return numberparsing::is_negative(peek_root_scalar("numbersign"));
}
simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
return numberparsing::is_integer(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
return numberparsing::get_number_type(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
number num;
error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
if(error) { return error; }
return num;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<haswell::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_inline value_iterator value_iterator::child() const noexcept {
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool value_iterator::at_end() const noexcept {
return _json_iter->at_end();
}
simdjson_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.position() == start_position();
}
simdjson_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
return _json_iter->error;
}
simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
return _json_iter->string_buf_loc();
}
simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
return *_json_iter;
}
simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
return *_json_iter;
}
simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(start_position());
}
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
logger::log_start_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
const uint8_t *json;
if (!is_at_start()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
return SUCCESS;
}
simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
return _json_iter->peek();
}
simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
logger::log_error(*_json_iter, start_position(), depth(), message);
return INCORRECT_TYPE;
}
simdjson_inline bool value_iterator::is_at_start() const noexcept {
return position() == start_position();
}
simdjson_inline bool value_iterator::is_at_key() const noexcept {
// Keys are at the same depth as the object.
// Note here that we could be safer and check that we are within an object,
// but we do not.
//
// As long as we are at the object's depth, in a valid document,
// we will only ever be at { , : or the actual string key: ".
return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
}
simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = position() - start_position();
return delta == 1 || delta == 2;
}
inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_inline void value_iterator::move_at_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position);
}
simdjson_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
// Unlike reenter_child(), this does not require the live depth to be
// exactly one level shallower than depth, nor does it validate against
// the parser's per-depth container-start bookkeeping: neither holds in
// general for a caller-supplied snapshot (see object_position). What
// must still always hold, regardless of what was captured or how far
// the live iterator has since moved, is that position and depth are
// themselves sane values -- this is the same bound reenter_child()
// itself applies unconditionally.
SIMDJSON_ASSUME(position != nullptr);
SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
_json_iter->_depth = depth;
_json_iter->token.set_position(position);
}
simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_array();
}
simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_object();
}
inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return json_type::unknown;
}
}
simdjson_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<haswell::ondemand::value_iterator>::simdjson_result(haswell::ondemand::value_iterator &&value) noexcept
: implementation_simdjson_result_base<haswell::ondemand::value_iterator>(std::forward<haswell::ondemand::value_iterator>(value)) {}
simdjson_inline simdjson_result<haswell::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<haswell::ondemand::value_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/value_iterator-inl.h for haswell */
/* including simdjson/generic/ondemand/serialization-inl.h for haswell: #include "simdjson/generic/ondemand/serialization-inl.h" */
/* begin file simdjson/generic/ondemand/serialization-inl.h for haswell */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-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/serialization.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace haswell::ondemand;
haswell::ondemand::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
haswell::ondemand::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
haswell::ondemand::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(haswell::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<haswell::ondemand::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace haswell { namespace ondemand {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::haswell::ondemand::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::haswell::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::haswell::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* end file simdjson/generic/ondemand/serialization-inl.h for haswell */
// JSON path accessor (compile-time) - must be after inline definitions
/* including simdjson/generic/ondemand/compile_time_accessors.h for haswell: #include "simdjson/generic/ondemand/compile_time_accessors.h" */
/* begin file simdjson/generic/ondemand/compile_time_accessors.h for haswell */
/**
* Compile-time JSON Path and JSON Pointer accessors using C++26 reflection (P2996)
*
* This file validates JSON paths/pointers against struct definitions at compile time
* and generates optimized accessor code with zero runtime overhead.
*
* ## How It Works
*
* **Compile Time**: Path is parsed, validated against struct, types are checked
* **Runtime**: Direct navigation with no parsing or validation overhead
*
* Example:
* ```cpp
* struct User { std::string name; std::vector<std::string> emails; };
*
* std::string email;
* path_accessor<User, ".emails[0]">::extract_field(doc, email);
*
* // Compile time validates:
* // 1. User has "emails" field
* // 2. "emails" is array-like
* // 3. Element type is std::string
* // 4. static_assert(^^std::string == ^^std::string)
*
* // Runtime just navigates:
* // doc.get_object().find_field("emails").get_array().at(0).get(email)
* ```
*
* ## Key Reflection APIs
*
* - `^^Type`: Reflect operator, converts type to std::meta::info
* - `std::meta::nonstatic_data_members_of(type)`: Get all fields of a struct
* - `std::meta::identifier_of(member)`: Get field name as string_view
* - `std::meta::type_of(member)`: Get reflected type of a field
* - `std::meta::is_array_type(type)`: Check if C-style array
* - `std::meta::remove_extent(array)`: Extract element type from array
* - `std::meta::members_of(type)`: Get all members including typedefs
* - `std::meta::is_type(member)`: Check if member is a type (vs field)
*
* All operations execute at compile time in consteval contexts.
*/
#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H */
/* amalgamation skipped (editor-only): */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
// is unlikely that we will have reflection support without concepts support.
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#include <string_view>
#include <cstddef>
#include <array>
namespace simdjson {
namespace haswell {
namespace ondemand {
/***
* JSONPath implementation for compile-time access
* RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
*/
namespace json_path {
// Note: value type must be fully defined before this header is included
// This is ensured by including this in amalgamated.h after value-inl.h
using ::simdjson::haswell::ondemand::value;
// Path step types
enum class step_type {
field, // .field_name or ["field_name"]
array_index // [index]
};
// Represents a single step in a JSON path
template<std::size_t N>
struct path_step {
step_type type;
char key[N]; // Field name (empty for array indices)
std::size_t index; // Array index (0 for field access)
constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
: type(t), index(idx) {
for (std::size_t i = 0; i < N; ++i) {
key[i] = k[i];
}
}
constexpr std::string_view key_view() const {
return {key, N - 1};
}
};
// Helper to create field step
template<std::size_t N>
consteval auto make_field_step(const char (&name)[N]) {
return path_step<N>(step_type::field, name, 0);
}
// Helper to create array index step
consteval auto make_index_step(std::size_t idx) {
return path_step<1>(step_type::array_index, "", idx);
}
// Parse state for compile-time JSON path parsing
struct parse_result {
bool success;
std::size_t pos;
std::string_view error_msg;
};
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
struct json_path_parser {
static constexpr std::string_view path_str = Path.view();
// Skip leading $ if present
static consteval std::size_t skip_root() {
if (!path_str.empty() && path_str[0] == '$') {
return 1;
}
return 0;
}
// Count the number of steps in the path at compile time
static consteval std::size_t count_steps() {
std::size_t count = 0;
std::size_t i = skip_root();
while (i < path_str.size()) {
if (path_str[i] == '.') {
// Field access: .field
++i;
if (i >= path_str.size()) break;
// Skip field name
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
++i;
}
++count;
} else if (path_str[i] == '[') {
// Array or bracket notation
++i;
if (i >= path_str.size()) break;
if (path_str[i] == '"' || path_str[i] == '\'') {
// Field access: ["field"] or ['field']
char quote = path_str[i];
++i;
while (i < path_str.size() && path_str[i] != quote) {
++i;
}
if (i < path_str.size()) ++i; // skip closing quote
if (i < path_str.size() && path_str[i] == ']') ++i;
} else {
// Array index: [0], [123]
while (i < path_str.size() && path_str[i] != ']') {
++i;
}
if (i < path_str.size()) ++i; // skip ]
}
++count;
} else {
++i;
}
}
return count;
}
// Parse a field name at compile time
static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
std::size_t len = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
out[len++] = path_str[i++];
}
out[len] = '\0';
return i;
}
// Parse an array index at compile time
static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
std::size_t index = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
index = index * 10 + (path_str[i] - '0');
++i;
}
return {i, index};
}
};
// Compile-time path accessor generator
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
using value = ::simdjson::haswell::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
// If T is a struct, validates the path at compile time
// If T is void, skips validation
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
// Validate path at compile time if T is a struct
if constexpr (std::is_class_v<T>) {
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
}
// Parse the path at compile time to build access steps
return access_impl<parser.skip_root()>(doc_or_val.get_value());
}
// Extract value at path directly into target with compile-time type validation
// Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
// Validate path exists in struct definition
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
// Get the type at the end of the path
constexpr auto final_type = get_final_type();
// Verify target type matches the field type
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
// All validation done at compile time - just navigate and extract
auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get the final type by walking the path through the struct type
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
// .field syntax
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) break;
if (path_view[i] == '"' || path_view[i] == '\'') {
// ["field"] syntax
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip quote
if (i < path_view.size() && path_view[i] == ']') ++i;
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else {
// [index] syntax - extract element type
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
current_type = get_element_type_reflected(current_type);
}
} else {
++i;
}
}
return current_type;
}
private:
// Walk path and extract directly into final field using compile-time reflection
template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
if (current.error()) return current.error();
// Base case: end of path - extract into target
if constexpr (PathPos >= path_view.size()) {
return current.get(target_ref);
}
// Field access: .field_name
else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
}
// Bracket notation: [index] or ["field"]
else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
constexpr auto elem_type = get_element_type_reflected(CurrentType);
static_assert(elem_type != ^^void, "Could not determine array element type");
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto elem_value = arr.at(index);
if constexpr (next_pos >= path_view.size()) {
return elem_value.get(target_ref);
} else {
return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
}
}
}
// Skip unexpected characters and continue
else {
return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
}
}
// Find member by name in reflected type
static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == name) {
return mem;
}
}
}
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if (current.error()) return current;
if constexpr (PathPos >= path_view.size()) {
return current;
} else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto next_value = arr.at(index);
return access_impl<next_pos>(next_value);
}
} else {
return access_impl<PathPos + 1>(current);
}
}
// Parse next field name
static consteval auto parse_next_field(std::size_t start) {
std::size_t i = start + 1;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
return std::make_tuple(field_name, i);
}
// Parse bracket notation: returns (is_field, field_name, next_pos, index)
static consteval auto parse_bracket(std::size_t start) {
std::size_t i = start + 1; // skip '['
if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
// Field access
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip closing quote
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(true, field_name, i, std::size_t(0));
} else {
// Array index
std::size_t index = 0;
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
index = index * 10 + (path_view[i] - '0');
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(false, std::string_view{}, i, index);
}
}
public:
// Check if reflected type is array-like (C-style array or indexable container)
// Uses reflection to test: 1) std::meta::is_array_type() for C arrays
// 2) std::meta::substitute() to test concepts::indexable_container concept
static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return true;
}
if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
}
return false;
}
// Extract element type from reflected array or container
// For C arrays: uses std::meta::remove_extent()
// For containers: finds value_type member using std::meta::members_of()
static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return std::meta::remove_extent(type_reflection);
}
auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::is_type(mem)) {
auto name = std::meta::identifier_of(mem);
if (name == "value_type") {
return mem;
}
}
}
return ^^void;
}
private:
// Check if type has member with given name
template<typename Type>
static consteval bool has_member(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return true;
}
}
return false;
}
// Get type of member by name
template<typename Type>
static consteval auto get_member_type(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return std::meta::type_of(mem);
}
}
return ^^void;
}
// Check if non-reflected type is array-like
template<typename Type>
static consteval bool is_container_type() {
using BaseType = std::remove_cvref_t<Type>;
if constexpr (requires { typename BaseType::value_type; }) {
return true;
}
if constexpr (std::is_array_v<BaseType>) {
return true;
}
return false;
}
// Extract element type from non-reflected container
template<typename Type>
using extract_element_type = std::conditional_t<
requires { typename std::remove_cvref_t<Type>::value_type; },
typename std::remove_cvref_t<Type>::value_type,
std::conditional_t<
std::is_array_v<std::remove_cvref_t<Type>>,
std::remove_extent_t<std::remove_cvref_t<Type>>,
void
>
>;
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) return false;
if (path_view[i] == '"' || path_view[i] == '\'') {
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i;
if (i < path_view.size() && path_view[i] == ']') ++i;
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else {
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
if (!is_array_like_reflected(current_type)) {
return false;
}
auto new_type = get_element_type_reflected(current_type);
if (new_type == ^^void) {
return false;
}
current_type = new_type;
}
} else {
++i;
}
}
return true;
}
};
// Compile-time path accessor with validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::haswell::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<T, Path>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::haswell::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<void, Path>;
return accessor::access(doc_or_val);
}
// ============================================================================
// JSON Pointer Compile-Time Support (RFC 6901)
// ============================================================================
// JSON Pointer parser: /field/0/nested (slash-separated)
template<constevalutil::fixed_string Pointer>
struct json_pointer_parser {
static constexpr std::string_view pointer_str = Pointer.view();
// Unescape token: ~0 -> ~, ~1 -> /
static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
out_len = 0;
for (std::size_t i = 0; i < src.size(); ++i) {
if (src[i] == '~' && i + 1 < src.size()) {
if (src[i + 1] == '0') {
dest[out_len++] = '~';
++i;
} else if (src[i + 1] == '1') {
dest[out_len++] = '/';
++i;
} else {
dest[out_len++] = src[i];
}
} else {
dest[out_len++] = src[i];
}
}
}
// Check if token is numeric
static consteval bool is_numeric(std::string_view token) {
if (token.empty()) return false;
if (token[0] == '0' && token.size() > 1) return false;
for (char c : token) {
if (c < '0' || c > '9') return false;
}
return true;
}
// Parse numeric token to index
static consteval std::size_t parse_index(std::string_view token) {
std::size_t result = 0;
for (char c : token) {
result = result * 10 + (c - '0');
}
return result;
}
// Count tokens in pointer
static consteval std::size_t count_tokens() {
if (pointer_str.empty() || pointer_str == "/") return 0;
std::size_t count = 0;
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
while (pos < pointer_str.size()) {
++count;
std::size_t next_slash = pointer_str.find('/', pos);
if (next_slash == std::string_view::npos) break;
pos = next_slash + 1;
}
return count;
}
// Get Nth token
static consteval std::string_view get_token(std::size_t token_index) {
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
std::size_t current_token = 0;
while (current_token < token_index) {
std::size_t next_slash = pointer_str.find('/', pos);
pos = next_slash + 1;
++current_token;
}
std::size_t token_end = pointer_str.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_str.size();
return pointer_str.substr(pos, token_end - pos);
}
};
// JSON Pointer accessor
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
using parser = json_pointer_parser<Pointer>;
static constexpr std::string_view pointer_view = Pointer.view();
static constexpr std::size_t token_count = parser::count_tokens();
// Validate pointer against struct definition
static consteval bool validate_pointer() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
// Extract token up to next /
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
return false;
}
current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
} else {
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) return false;
}
pos = token_end + 1;
}
return true;
}
// Recursive accessor
template<std::size_t TokenIndex>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if constexpr (TokenIndex >= token_count) {
return current;
} else {
constexpr std::string_view token = parser::get_token(TokenIndex);
if constexpr (parser::is_numeric(token)) {
constexpr std::size_t index = parser::parse_index(token);
auto arr = current.get_array().value_unsafe();
auto next_value = arr.at(index);
return access_impl<TokenIndex + 1>(next_value);
} else {
auto obj = current.get_object().value_unsafe();
auto next_value = obj.find_field_unordered(token);
return access_impl<TokenIndex + 1>(next_value);
}
}
}
// Access JSON value at pointer
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
if constexpr (std::is_class_v<T>) {
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
}
if (pointer_view.empty() || pointer_view == "/") {
if constexpr (requires { doc_or_val.get_value(); }) {
return doc_or_val.get_value();
} else {
return doc_or_val;
}
}
simdjson_result<value> current = doc_or_val.get_value();
return access_impl<0>(current);
}
// Extract value at pointer directly into target with type validation
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
constexpr auto final_type = get_final_type();
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
simdjson_result<value> current_value = doc_or_val.get_value();
auto json_value = access_impl<0>(current_value);
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get final type by walking pointer through struct
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
} else {
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
break;
}
}
}
pos = token_end + 1;
}
return current_type;
}
};
// Compile-time JSON Pointer accessor with validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::haswell::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<T, Pointer>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::haswell::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<void, Pointer>;
return accessor::access(doc_or_val);
}
} // namespace json_path
} // namespace ondemand
} // namespace haswell
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* end file simdjson/generic/ondemand/compile_time_accessors.h for haswell */
/* end file simdjson/generic/ondemand/amalgamated.h for haswell */
/* including simdjson/haswell/end.h: #include "simdjson/haswell/end.h" */
/* begin file simdjson/haswell/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/haswell/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "haswell" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/haswell/end.h */
#endif // SIMDJSON_HASWELL_ONDEMAND_H
/* end file simdjson/haswell/ondemand.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(icelake)
/* including simdjson/icelake/ondemand.h: #include "simdjson/icelake/ondemand.h" */
/* begin file simdjson/icelake/ondemand.h */
#ifndef SIMDJSON_ICELAKE_ONDEMAND_H
#define SIMDJSON_ICELAKE_ONDEMAND_H
/* including simdjson/icelake/begin.h: #include "simdjson/icelake/begin.h" */
/* begin file simdjson/icelake/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "icelake" */
#define SIMDJSON_IMPLEMENTATION icelake
/* including simdjson/icelake/base.h: #include "simdjson/icelake/base.h" */
/* begin file simdjson/icelake/base.h */
#ifndef SIMDJSON_ICELAKE_BASE_H
#define SIMDJSON_ICELAKE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE
namespace simdjson {
/**
* Implementation for Icelake (Intel AVX512).
*/
namespace icelake {
class implementation;
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BASE_H
/* end file simdjson/icelake/base.h */
/* including simdjson/icelake/intrinsics.h: #include "simdjson/icelake/intrinsics.h" */
/* begin file simdjson/icelake/intrinsics.h */
#ifndef SIMDJSON_ICELAKE_INTRINSICS_H
#define SIMDJSON_ICELAKE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
* e.g., if __AVX2__ is set... in turn, we normally set these
* macros by compiling against the corresponding architecture
* (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
* software with these advanced instructions. In simdjson, we
* want to compile the whole program for a generic target,
* and only target our specific kernels. As a workaround,
* we directly include the needed headers. These headers would
* normally guard against such usage, but we carefully included
* <x86intrin.h> (or <intrin.h>) before, so the headers
* are fooled.
*/
#include <bmiintrin.h> // for _blsr_u64
#include <lzcntintrin.h> // for __lzcnt64
#include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
#include <smmintrin.h>
#include <tmmintrin.h>
#include <avxintrin.h>
#include <avx2intrin.h>
#include <wmmintrin.h> // for _mm_clmulepi64_si128
// Important: we need the AVX-512 headers:
#include <avx512fintrin.h>
#include <avx512dqintrin.h>
#include <avx512cdintrin.h>
#include <avx512bwintrin.h>
#include <avx512vlintrin.h>
#include <avx512vbmiintrin.h>
#include <avx512vbmi2intrin.h>
// unfortunately, we may not get _blsr_u64, but, thankfully, clang
// has it as a macro.
#ifndef _blsr_u64
// we roll our own
#define _blsr_u64(n) ((n - 1) & n)
#endif // _blsr_u64
#endif // SIMDJSON_CLANG_VISUAL_STUDIO
static_assert(sizeof(__m512i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for icelake");
#endif // SIMDJSON_ICELAKE_INTRINSICS_H
/* end file simdjson/icelake/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_ICELAKE
SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,pclmul,lzcnt,popcnt")
#endif
/* including simdjson/icelake/bitmanipulation.h: #include "simdjson/icelake/bitmanipulation.h" */
/* begin file simdjson/icelake/bitmanipulation.h */
#ifndef SIMDJSON_ICELAKE_BITMANIPULATION_H
#define SIMDJSON_ICELAKE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
return (int)_tzcnt_u64(input_num);
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
////////
// You might expect the next line to be equivalent to
// return (int)_tzcnt_u64(input_num);
// but the generated code differs and might be less efficient?
////////
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return _blsr_u64(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return int(_lzcnt_u64(input_num));
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BITMANIPULATION_H
/* end file simdjson/icelake/bitmanipulation.h */
/* including simdjson/icelake/bitmask.h: #include "simdjson/icelake/bitmask.h" */
/* begin file simdjson/icelake/bitmask.h */
#ifndef SIMDJSON_ICELAKE_BITMASK_H
#define SIMDJSON_ICELAKE_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processor supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_BITMASK_H
/* end file simdjson/icelake/bitmask.h */
/* including simdjson/icelake/simd.h: #include "simdjson/icelake/simd.h" */
/* begin file simdjson/icelake/simd.h */
#ifndef SIMDJSON_ICELAKE_SIMD_H
#define SIMDJSON_ICELAKE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if defined(__GNUC__) && !defined(__clang__)
#if __GNUC__ == 8
#define SIMDJSON_GCC8 1
#endif // __GNUC__ == 8
#endif // defined(__GNUC__) && !defined(__clang__)
#if SIMDJSON_GCC8
/**
* GCC 8 fails to provide _mm512_set_epi8. We roll our own.
*/
inline __m512i _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) {
return _mm512_set_epi64(uint64_t(a7) + (uint64_t(a6) << 8) + (uint64_t(a5) << 16) + (uint64_t(a4) << 24) + (uint64_t(a3) << 32) + (uint64_t(a2) << 40) + (uint64_t(a1) << 48) + (uint64_t(a0) << 56),
uint64_t(a15) + (uint64_t(a14) << 8) + (uint64_t(a13) << 16) + (uint64_t(a12) << 24) + (uint64_t(a11) << 32) + (uint64_t(a10) << 40) + (uint64_t(a9) << 48) + (uint64_t(a8) << 56),
uint64_t(a23) + (uint64_t(a22) << 8) + (uint64_t(a21) << 16) + (uint64_t(a20) << 24) + (uint64_t(a19) << 32) + (uint64_t(a18) << 40) + (uint64_t(a17) << 48) + (uint64_t(a16) << 56),
uint64_t(a31) + (uint64_t(a30) << 8) + (uint64_t(a29) << 16) + (uint64_t(a28) << 24) + (uint64_t(a27) << 32) + (uint64_t(a26) << 40) + (uint64_t(a25) << 48) + (uint64_t(a24) << 56),
uint64_t(a39) + (uint64_t(a38) << 8) + (uint64_t(a37) << 16) + (uint64_t(a36) << 24) + (uint64_t(a35) << 32) + (uint64_t(a34) << 40) + (uint64_t(a33) << 48) + (uint64_t(a32) << 56),
uint64_t(a47) + (uint64_t(a46) << 8) + (uint64_t(a45) << 16) + (uint64_t(a44) << 24) + (uint64_t(a43) << 32) + (uint64_t(a42) << 40) + (uint64_t(a41) << 48) + (uint64_t(a40) << 56),
uint64_t(a55) + (uint64_t(a54) << 8) + (uint64_t(a53) << 16) + (uint64_t(a52) << 24) + (uint64_t(a51) << 32) + (uint64_t(a50) << 40) + (uint64_t(a49) << 48) + (uint64_t(a48) << 56),
uint64_t(a63) + (uint64_t(a62) << 8) + (uint64_t(a61) << 16) + (uint64_t(a60) << 24) + (uint64_t(a59) << 32) + (uint64_t(a58) << 40) + (uint64_t(a57) << 48) + (uint64_t(a56) << 56));
}
#endif // SIMDJSON_GCC8
namespace simdjson {
namespace icelake {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m512i value;
// Zero constructor
simdjson_inline base() : value{__m512i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m512i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m512i&() const { return this->value; }
simdjson_inline operator __m512i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm512_or_si512(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm512_and_si512(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm512_xor_si512(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm512_andnot_si512(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint32_t bitmask_t;
typedef uint64_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m512i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline uint64_t operator==(const simd8<T> lhs, const simd8<T> rhs) {
return _mm512_cmpeq_epi8_mask(lhs, rhs);
}
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
// workaround for compilers unable to figure out that 16 - N is a constant (GCC 8)
constexpr int shift = 16 - N;
return _mm512_alignr_epi8(*this, _mm512_permutex2var_epi64(prev_chunk, _mm512_set_epi64(13, 12, 11, 10, 9, 8, 7, 6), *this), shift);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m512i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm512_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm512_setzero_si512(); }
static simdjson_inline simd8<T> load(const T values[64]) {
return _mm512_loadu_si512(reinterpret_cast<const __m512i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m512i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm512_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm512_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm512_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 64 - count_ones(mask) bytes of the result are significant but 64 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint64_t mask, L * output) const {
// we deliberately avoid _mm512_mask_compressstoreu_epi8 for portability
// (AMD Zen4 has terrible performance with it, it is effectively broken)
// _mm512_mask_compressstoreu_epi8 (output,~mask,*this);
__m512i compressed = _mm512_maskz_compress_epi8(~mask, *this);
_mm512_storeu_si512(output, compressed); // could use a mask
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m512i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[64]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31,
int8_t v32, int8_t v33, int8_t v34, int8_t v35, int8_t v36, int8_t v37, int8_t v38, int8_t v39,
int8_t v40, int8_t v41, int8_t v42, int8_t v43, int8_t v44, int8_t v45, int8_t v46, int8_t v47,
int8_t v48, int8_t v49, int8_t v50, int8_t v51, int8_t v52, int8_t v53, int8_t v54, int8_t v55,
int8_t v56, int8_t v57, int8_t v58, int8_t v59, int8_t v60, int8_t v61, int8_t v62, int8_t v63
) : simd8(_mm512_set_epi8(
v63, v62, v61, v60, v59, v58, v57, v56,
v55, v54, v53, v52, v51, v50, v49, v48,
v47, v46, v45, v44, v43, v42, v41, v40,
v39, v38, v37, v36, v35, v34, v33, v32,
v31, v30, v29, v28, v27, v26, v25, v24,
v23, v22, v21, v20, v19, v18, v17, v16,
v15, v14, v13, v12, v11, v10, v9, v8,
v7, v6, v5, v4, v3, v2, v1, v0
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm512_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm512_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m512i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[64]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31,
uint8_t v32, uint8_t v33, uint8_t v34, uint8_t v35, uint8_t v36, uint8_t v37, uint8_t v38, uint8_t v39,
uint8_t v40, uint8_t v41, uint8_t v42, uint8_t v43, uint8_t v44, uint8_t v45, uint8_t v46, uint8_t v47,
uint8_t v48, uint8_t v49, uint8_t v50, uint8_t v51, uint8_t v52, uint8_t v53, uint8_t v54, uint8_t v55,
uint8_t v56, uint8_t v57, uint8_t v58, uint8_t v59, uint8_t v60, uint8_t v61, uint8_t v62, uint8_t v63
) : simd8(_mm512_set_epi8(
v63, v62, v61, v60, v59, v58, v57, v56,
v55, v54, v53, v52, v51, v50, v49, v48,
v47, v46, v45, v44, v43, v42, v41, v40,
v39, v38, v37, v36, v35, v34, v33, v32,
v31, v30, v29, v28, v27, v26, v25, v24,
v23, v22, v21, v20, v19, v18, v17, v16,
v15, v14, v13, v12, v11, v10, v9, v8,
v7, v6, v5, v4, v3, v2, v1, v0
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm512_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm512_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm512_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm512_min_epu8(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline uint64_t operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline uint64_t operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm512_movepi8_mask(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const {
return !_mm512_test_epi8_mask(*this, *this);
}
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return !_mm512_test_epi8_mask(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm512_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const simd8<T> chunk0) : chunks{chunk0} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(mask, output);
return 64 - count_ones(mask);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0];
}
simdjson_inline simd8x64<T> bit_or(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<T>(
this->chunks[0] | mask
);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return this->chunks[0] == mask;
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return this->chunks[0] == other.chunks[0];
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return this->chunks[0] <= mask;
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_SIMD_H
/* end file simdjson/icelake/simd.h */
/* including simdjson/icelake/stringparsing_defs.h: #include "simdjson/icelake/stringparsing_defs.h" */
/* begin file simdjson/icelake/stringparsing_defs.h */
#ifndef SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
#define SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint64_t bs_bits;
uint64_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 15 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
// store to dest unconditionally - we can overwrite the bits we don't like later
v.store(dst);
return {
static_cast<uint64_t>(v == '\\'), // bs_bits
static_cast<uint64_t>(v == '"'), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
__mmask64 escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
__mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
__mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
__mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
return {
(is_backslash | is_quote | is_control)
};
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_ICELAKE_STRINGPARSING_DEFS_H
/* end file simdjson/icelake/stringparsing_defs.h */
/* including simdjson/icelake/numberparsing_defs.h: #include "simdjson/icelake/numberparsing_defs.h" */
/* begin file simdjson/icelake/numberparsing_defs.h */
#ifndef SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
#define SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace numberparsing {
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace icelake
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_ICELAKE_NUMBERPARSING_DEFS_H
/* end file simdjson/icelake/numberparsing_defs.h */
/* end file simdjson/icelake/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for icelake: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for icelake */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for icelake: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::icelake::number_type */
using number_type = simdjson::icelake::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for icelake */
/* including simdjson/generic/ondemand/deserialize.h for icelake: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for icelake */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<icelake::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<icelake::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<icelake::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<icelake::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = icelake::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = icelake::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = icelake::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = icelake::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = icelake::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = icelake::ondemand::array;
using object_type = icelake::ondemand::object;
using value_type = icelake::ondemand::value;
using document_type = icelake::ondemand::document;
using document_reference_type = icelake::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for icelake */
/* including simdjson/generic/ondemand/value_iterator.h for icelake: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::value_iterator> : public icelake::implementation_simdjson_result_base<icelake::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(icelake::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for icelake */
/* including simdjson/generic/ondemand/value.h for icelake: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::value> : public icelake::implementation_simdjson_result_base<icelake::ondemand::value> {
public:
simdjson_inline simdjson_result(icelake::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<icelake::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator icelake::ondemand::array() noexcept(false);
simdjson_inline operator icelake::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator icelake::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<icelake::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<icelake::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<icelake::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<icelake::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<icelake::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<icelake::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<icelake::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<icelake::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<icelake::ondemand::value>::get<icelake::ondemand::value>(
icelake::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<icelake::ondemand::value>
simdjson_result<icelake::ondemand::value>::get<icelake::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for icelake */
/* including simdjson/generic/ondemand/logger.h for icelake: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for icelake */
/* including simdjson/generic/ondemand/token_iterator.h for icelake: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::token_iterator> : public icelake::implementation_simdjson_result_base<icelake::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(icelake::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for icelake */
/* including simdjson/generic/ondemand/json_iterator.h for icelake: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::json_iterator> : public icelake::implementation_simdjson_result_base<icelake::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(icelake::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for icelake */
/* including simdjson/generic/ondemand/json_type.h for icelake: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::json_type> : public icelake::implementation_simdjson_result_base<icelake::ondemand::json_type> {
public:
simdjson_inline simdjson_result(icelake::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for icelake */
/* including simdjson/generic/ondemand/raw_json_string.h for icelake: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::raw_json_string> : public icelake::implementation_simdjson_result_base<icelake::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(icelake::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(icelake::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(icelake::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for icelake */
/* including simdjson/generic/ondemand/parser.h for icelake: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::parser> : public icelake::implementation_simdjson_result_base<icelake::ondemand::parser> {
public:
simdjson_inline simdjson_result(icelake::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for icelake */
// All other declarations
/* including simdjson/generic/ondemand/array.h for icelake: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::array> : public icelake::implementation_simdjson_result_base<icelake::ondemand::array> {
public:
simdjson_inline simdjson_result(icelake::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, icelake::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, icelake::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, icelake::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, icelake::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for icelake */
/* including simdjson/generic/ondemand/array_iterator.h for icelake: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::array_iterator> : public icelake::implementation_simdjson_result_base<icelake::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<icelake::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(icelake::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<icelake::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<icelake::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<icelake::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for icelake */
/* including simdjson/generic/ondemand/document.h for icelake: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::document> : public icelake::implementation_simdjson_result_base<icelake::ondemand::document> {
public:
simdjson_inline simdjson_result(icelake::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using icelake::implementation_simdjson_result_base<icelake::ondemand::document>::operator*;
using icelake::implementation_simdjson_result_base<icelake::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, icelake::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator icelake::ondemand::array() & noexcept(false);
simdjson_inline operator icelake::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator icelake::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator icelake::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<icelake::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<icelake::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::document_reference> : public icelake::implementation_simdjson_result_base<icelake::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(icelake::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator icelake::ondemand::array() & noexcept(false);
simdjson_inline operator icelake::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator icelake::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator icelake::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<icelake::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<icelake::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for icelake */
/* including simdjson/generic/ondemand/document_stream.h for icelake: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace icelake {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::document_stream> : public icelake::implementation_simdjson_result_base<icelake::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(icelake::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for icelake */
/* including simdjson/generic/ondemand/field.h for icelake: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::field> : public icelake::implementation_simdjson_result_base<icelake::ondemand::field> {
public:
simdjson_inline simdjson_result(icelake::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<icelake::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for icelake */
/* including simdjson/generic/ondemand/key_selector.h for icelake: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace icelake {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for icelake */
/* including simdjson/generic/ondemand/object.h for icelake: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::object> : public icelake::implementation_simdjson_result_base<icelake::ondemand::object> {
public:
simdjson_inline simdjson_result(icelake::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<icelake::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<icelake::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<icelake::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<icelake::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(icelake::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, icelake::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, icelake::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, icelake::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, icelake::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires icelake::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, icelake::ondemand::value>
simdjson_inline icelake::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, icelake::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires icelake::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(icelake::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline icelake::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(icelake::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(icelake::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline icelake::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(icelake::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for icelake */
/* including simdjson/generic/ondemand/object_iterator.h for icelake: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<icelake::ondemand::object_iterator> : public icelake::implementation_simdjson_result_base<icelake::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(icelake::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<icelake::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<icelake::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<icelake::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<icelake::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for icelake */
/* including simdjson/generic/ondemand/ranges.h for icelake: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::icelake::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::icelake::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for icelake */
/* including simdjson/generic/ondemand/serialization.h for icelake: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, icelake::ondemand::document> &&
!std::same_as<T, icelake::ondemand::value> &&
!std::same_as<T, icelake::ondemand::object> &&
!std::same_as<T, icelake::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace icelake { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::object> x);
#endif
}}} // namespace simdjson::icelake::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for icelake */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for icelake: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for icelake */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
icelake::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, icelake::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
icelake::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, icelake::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
icelake::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, icelake::ondemand::value &val, T &out) noexcept(false) {
icelake::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, icelake::ondemand::document &doc, T &out) noexcept(false) {
icelake::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, icelake::ondemand::document_reference &doc, T &out) noexcept(false) {
icelake::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, icelake::ondemand::number>
&& !std::is_same_v<T, icelake::ondemand::document>
&& !std::is_same_v<T, icelake::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^icelake::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = icelake::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, icelake::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
icelake::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
icelake::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
icelake::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
icelake::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, icelake::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
icelake::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, icelake::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, icelake::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, icelake::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for icelake */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for icelake: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::array>::simdjson_result(
icelake::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<icelake::ondemand::array>(
std::forward<icelake::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<icelake::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<icelake::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<icelake::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<icelake::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for icelake */
/* including simdjson/generic/ondemand/array_iterator-inl.h for icelake: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::array_iterator>::simdjson_result(
icelake::ondemand::array_iterator &&value
) noexcept
: icelake::implementation_simdjson_result_base<icelake::ondemand::array_iterator>(std::forward<icelake::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: icelake::implementation_simdjson_result_base<icelake::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<icelake::ondemand::array_iterator>::operator==(const simdjson_result<icelake::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<icelake::ondemand::array_iterator>::operator!=(const simdjson_result<icelake::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> &simdjson_result<icelake::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<icelake::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for icelake */
/* including simdjson/generic/ondemand/value-inl.h for icelake: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace icelake {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::value>::simdjson_result(
icelake::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<icelake::ondemand::value>(
std::forward<icelake::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<icelake::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<icelake::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<icelake::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<icelake::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<icelake::ondemand::array> simdjson_result<icelake::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<icelake::ondemand::object> simdjson_result<icelake::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<icelake::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<icelake::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<icelake::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<icelake::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<icelake::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<icelake::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<icelake::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<icelake::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<icelake::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<icelake::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<icelake::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<icelake::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<icelake::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<icelake::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<icelake::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<icelake::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<icelake::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<icelake::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> simdjson_result<icelake::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<icelake::ondemand::value>::get<icelake::ondemand::value>(icelake::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<icelake::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<icelake::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::get<icelake::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<icelake::ondemand::json_type> simdjson_result<icelake::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<icelake::number_type> simdjson_result<icelake::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<icelake::ondemand::number> simdjson_result<icelake::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<icelake::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<icelake::ondemand::value>::operator icelake::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::value>::operator icelake::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::value>::operator icelake::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<icelake::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<icelake::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<icelake::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for icelake */
/* including simdjson/generic/ondemand/document-inl.h for icelake: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace icelake {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::document>::simdjson_result(
icelake::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<icelake::ondemand::document>(
std::forward<icelake::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<icelake::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<icelake::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<icelake::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<icelake::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<icelake::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<icelake::ondemand::array> simdjson_result<icelake::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<icelake::ondemand::object> simdjson_result<icelake::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<icelake::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<icelake::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<icelake::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<icelake::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<icelake::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<icelake::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<icelake::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<icelake::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<icelake::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<icelake::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<icelake::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<icelake::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<icelake::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<icelake::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<icelake::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<icelake::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<icelake::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> simdjson_result<icelake::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<icelake::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<icelake::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<icelake::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<icelake::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<icelake::ondemand::document> simdjson_result<icelake::ondemand::document>::get<icelake::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<icelake::ondemand::document> simdjson_result<icelake::ondemand::document>::get<icelake::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<icelake::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document>::get<icelake::ondemand::document>(icelake::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document>::get<icelake::ondemand::document>(icelake::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<icelake::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<icelake::ondemand::json_type> simdjson_result<icelake::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<icelake::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<icelake::number_type> simdjson_result<icelake::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<icelake::ondemand::number> simdjson_result<icelake::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, icelake::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<icelake::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator icelake::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator icelake::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator icelake::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<icelake::ondemand::document>::operator icelake::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<icelake::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<icelake::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<icelake::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<icelake::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace icelake {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::simdjson_result(icelake::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<icelake::ondemand::document_reference>(std::forward<icelake::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<icelake::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<icelake::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<icelake::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<icelake::ondemand::array_iterator> simdjson_result<icelake::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<icelake::ondemand::array> simdjson_result<icelake::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<icelake::ondemand::object> simdjson_result<icelake::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<icelake::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<icelake::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<icelake::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<icelake::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<icelake::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<icelake::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<icelake::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<icelake::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<icelake::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<icelake::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<icelake::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<icelake::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<icelake::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<icelake::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<icelake::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<icelake::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<icelake::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> simdjson_result<icelake::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<icelake::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<icelake::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<icelake::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, icelake::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<icelake::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<icelake::ondemand::json_type> simdjson_result<icelake::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document_reference>::get(icelake::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document_reference>::get(icelake::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<icelake::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<icelake::number_type> simdjson_result<icelake::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<icelake::ondemand::number> simdjson_result<icelake::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, icelake::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, icelake::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator icelake::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator icelake::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator icelake::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<icelake::ondemand::document_reference>::operator icelake::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<icelake::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<icelake::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for icelake */
/* including simdjson/generic/ondemand/document_stream-inl.h for icelake: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace icelake {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<icelake::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<icelake::ondemand::document_stream>::simdjson_result(
icelake::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<icelake::ondemand::document_stream>(
std::forward<icelake::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for icelake */
/* including simdjson/generic/ondemand/field-inl.h for icelake: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::field>::simdjson_result(
icelake::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<icelake::ondemand::field>(
std::forward<icelake::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<icelake::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<icelake::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string> simdjson_result<icelake::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<icelake::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<icelake::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<icelake::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for icelake */
/* including simdjson/generic/ondemand/json_iterator-inl.h for icelake: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::json_iterator>::simdjson_result(icelake::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<icelake::ondemand::json_iterator>(std::forward<icelake::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<icelake::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<icelake::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for icelake */
/* including simdjson/generic/ondemand/json_type-inl.h for icelake: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::json_type>::simdjson_result(icelake::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<icelake::ondemand::json_type>(std::forward<icelake::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<icelake::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<icelake::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for icelake */
/* including simdjson/generic/ondemand/logger-inl.h for icelake: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace icelake {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for icelake */
/* including simdjson/generic/ondemand/object-inl.h for icelake: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::object>::simdjson_result(icelake::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<icelake::ondemand::object>(std::forward<icelake::ondemand::object>(value)) {}
simdjson_inline simdjson_result<icelake::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<icelake::ondemand::object>(error) {}
simdjson_inline simdjson_result<icelake::ondemand::object_iterator> simdjson_result<icelake::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<icelake::ondemand::object_iterator> simdjson_result<icelake::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<icelake::ondemand::object_iterator> simdjson_result<icelake::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<icelake::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<icelake::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<icelake::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<icelake::ondemand::value> simdjson_result<icelake::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, icelake::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<icelake::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires icelake::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, icelake::ondemand::value>
simdjson_inline icelake::ondemand::for_each_result
simdjson_result<icelake::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, icelake::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires icelake::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(icelake::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline icelake::ondemand::for_each_result
simdjson_result<icelake::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(icelake::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(icelake::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline icelake::ondemand::for_each_result
simdjson_result<icelake::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(icelake::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<icelake::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<icelake::ondemand::object_position> simdjson_result<icelake::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<icelake::ondemand::object>::revert_position(icelake::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<icelake::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<icelake::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<icelake::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for icelake */
/* including simdjson/generic/ondemand/object_iterator-inl.h for icelake: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::object_iterator>::simdjson_result(
icelake::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<icelake::ondemand::object_iterator>(std::forward<icelake::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<icelake::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<icelake::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<icelake::ondemand::field> simdjson_result<icelake::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<icelake::ondemand::object_iterator>::operator==(const simdjson_result<icelake::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<icelake::ondemand::object_iterator>::operator!=(const simdjson_result<icelake::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<icelake::ondemand::object_iterator> &simdjson_result<icelake::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for icelake */
/* including simdjson/generic/ondemand/ranges-inl.h for icelake: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace icelake {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::icelake::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::icelake::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::icelake::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::icelake::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::icelake::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::icelake::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for icelake */
/* including simdjson/generic/ondemand/parser-inl.h for icelake: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::parser>::simdjson_result(icelake::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<icelake::ondemand::parser>(std::forward<icelake::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<icelake::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<icelake::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for icelake */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for icelake: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string>::simdjson_result(icelake::ondemand::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<icelake::ondemand::raw_json_string>(std::forward<icelake::ondemand::raw_json_string>(value)) {}
simdjson_inline simdjson_result<icelake::ondemand::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<icelake::ondemand::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<icelake::ondemand::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline char simdjson_result<icelake::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
if (error()) { return error(); }
return first[i];
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<icelake::ondemand::raw_json_string>::unescape(icelake::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<icelake::ondemand::raw_json_string>::unescape_wobbly(icelake::ondemand::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* end file simdjson/generic/ondemand/raw_json_string-inl.h for icelake */
/* including simdjson/generic/ondemand/token_iterator-inl.h for icelake: #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/token_iterator-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::token_iterator>::simdjson_result(icelake::ondemand::token_iterator &&value) noexcept
: implementation_simdjson_result_base<icelake::ondemand::token_iterator>(std::forward<icelake::ondemand::token_iterator>(value)) {}
simdjson_inline simdjson_result<icelake::ondemand::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<icelake::ondemand::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/token_iterator-inl.h for icelake */
/* including simdjson/generic/ondemand/value_iterator-inl.h for icelake: #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/value_iterator-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace icelake {
namespace ondemand {
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
bool escaping{false};
for (uint32_t i = 1; i < max_len; i++) {
switch (json[i]) {
case '"':
if (!escaping) { return true; }
escaping = false;
break;
case '\\':
escaping = !escaping;
break;
default:
escaping = false;
break;
}
}
return false;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
SIMDJSON_TRY(end_container());
return false;
}
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
// call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != '}') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
// If the last character is } *and* the first gibberish character is also '}'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed object.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
auto error = check_root_object();
if(error) { return error; }
return started_object();
}
simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
#if SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
return false;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return false;
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
// If someone queried a key but they not did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
//
// ```
// // At the beginning of a field
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // At the end of the object
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// ```
//
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
SIMDJSON_TRY(reset_object().get(has_value));
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
return raw_json_string(key);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
assert_at_next();
const uint8_t *k = _json_iter->return_current_and_advance();
if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
// After return_current_and_advance(), the current token is the ':' that follows
// the key. The closing quote sits just before it (only JSON whitespace may
// intervene), so step back from the ':' to the closing quote to get the length.
// In minified JSON this is a single back-step.
const char *q = reinterpret_cast<const char *>(_json_iter->peek());
do { --q; } while (*q != '"');
key = raw_json_string(k);
len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
assert_at_next();
if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
answer += std::string(" ]");
return answer;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
SIMDJSON_TRY( end_container() );
return false;
}
_json_iter->descend_to(depth()+1);
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
// also call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != ']') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
// If the last character is ] *and* the first gibberish character is also ']'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed array.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
auto error = check_root_array();
if (error) { return error; }
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
}
simdjson_inline bool value_iterator::is_root_negative() noexcept {
return numberparsing::is_negative(peek_root_scalar("numbersign"));
}
simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
return numberparsing::is_integer(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
return numberparsing::get_number_type(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
number num;
error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
if(error) { return error; }
return num;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<icelake::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_inline value_iterator value_iterator::child() const noexcept {
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool value_iterator::at_end() const noexcept {
return _json_iter->at_end();
}
simdjson_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.position() == start_position();
}
simdjson_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
return _json_iter->error;
}
simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
return _json_iter->string_buf_loc();
}
simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
return *_json_iter;
}
simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
return *_json_iter;
}
simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(start_position());
}
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
logger::log_start_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
const uint8_t *json;
if (!is_at_start()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
return SUCCESS;
}
simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
return _json_iter->peek();
}
simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
logger::log_error(*_json_iter, start_position(), depth(), message);
return INCORRECT_TYPE;
}
simdjson_inline bool value_iterator::is_at_start() const noexcept {
return position() == start_position();
}
simdjson_inline bool value_iterator::is_at_key() const noexcept {
// Keys are at the same depth as the object.
// Note here that we could be safer and check that we are within an object,
// but we do not.
//
// As long as we are at the object's depth, in a valid document,
// we will only ever be at { , : or the actual string key: ".
return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
}
simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = position() - start_position();
return delta == 1 || delta == 2;
}
inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_inline void value_iterator::move_at_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position);
}
simdjson_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
// Unlike reenter_child(), this does not require the live depth to be
// exactly one level shallower than depth, nor does it validate against
// the parser's per-depth container-start bookkeeping: neither holds in
// general for a caller-supplied snapshot (see object_position). What
// must still always hold, regardless of what was captured or how far
// the live iterator has since moved, is that position and depth are
// themselves sane values -- this is the same bound reenter_child()
// itself applies unconditionally.
SIMDJSON_ASSUME(position != nullptr);
SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
_json_iter->_depth = depth;
_json_iter->token.set_position(position);
}
simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_array();
}
simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_object();
}
inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return json_type::unknown;
}
}
simdjson_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<icelake::ondemand::value_iterator>::simdjson_result(icelake::ondemand::value_iterator &&value) noexcept
: implementation_simdjson_result_base<icelake::ondemand::value_iterator>(std::forward<icelake::ondemand::value_iterator>(value)) {}
simdjson_inline simdjson_result<icelake::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<icelake::ondemand::value_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/value_iterator-inl.h for icelake */
/* including simdjson/generic/ondemand/serialization-inl.h for icelake: #include "simdjson/generic/ondemand/serialization-inl.h" */
/* begin file simdjson/generic/ondemand/serialization-inl.h for icelake */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-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/serialization.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace icelake::ondemand;
icelake::ondemand::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
icelake::ondemand::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
icelake::ondemand::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(icelake::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<icelake::ondemand::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace icelake { namespace ondemand {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::icelake::ondemand::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::icelake::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::icelake::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* end file simdjson/generic/ondemand/serialization-inl.h for icelake */
// JSON path accessor (compile-time) - must be after inline definitions
/* including simdjson/generic/ondemand/compile_time_accessors.h for icelake: #include "simdjson/generic/ondemand/compile_time_accessors.h" */
/* begin file simdjson/generic/ondemand/compile_time_accessors.h for icelake */
/**
* Compile-time JSON Path and JSON Pointer accessors using C++26 reflection (P2996)
*
* This file validates JSON paths/pointers against struct definitions at compile time
* and generates optimized accessor code with zero runtime overhead.
*
* ## How It Works
*
* **Compile Time**: Path is parsed, validated against struct, types are checked
* **Runtime**: Direct navigation with no parsing or validation overhead
*
* Example:
* ```cpp
* struct User { std::string name; std::vector<std::string> emails; };
*
* std::string email;
* path_accessor<User, ".emails[0]">::extract_field(doc, email);
*
* // Compile time validates:
* // 1. User has "emails" field
* // 2. "emails" is array-like
* // 3. Element type is std::string
* // 4. static_assert(^^std::string == ^^std::string)
*
* // Runtime just navigates:
* // doc.get_object().find_field("emails").get_array().at(0).get(email)
* ```
*
* ## Key Reflection APIs
*
* - `^^Type`: Reflect operator, converts type to std::meta::info
* - `std::meta::nonstatic_data_members_of(type)`: Get all fields of a struct
* - `std::meta::identifier_of(member)`: Get field name as string_view
* - `std::meta::type_of(member)`: Get reflected type of a field
* - `std::meta::is_array_type(type)`: Check if C-style array
* - `std::meta::remove_extent(array)`: Extract element type from array
* - `std::meta::members_of(type)`: Get all members including typedefs
* - `std::meta::is_type(member)`: Check if member is a type (vs field)
*
* All operations execute at compile time in consteval contexts.
*/
#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H */
/* amalgamation skipped (editor-only): */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
// is unlikely that we will have reflection support without concepts support.
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#include <string_view>
#include <cstddef>
#include <array>
namespace simdjson {
namespace icelake {
namespace ondemand {
/***
* JSONPath implementation for compile-time access
* RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
*/
namespace json_path {
// Note: value type must be fully defined before this header is included
// This is ensured by including this in amalgamated.h after value-inl.h
using ::simdjson::icelake::ondemand::value;
// Path step types
enum class step_type {
field, // .field_name or ["field_name"]
array_index // [index]
};
// Represents a single step in a JSON path
template<std::size_t N>
struct path_step {
step_type type;
char key[N]; // Field name (empty for array indices)
std::size_t index; // Array index (0 for field access)
constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
: type(t), index(idx) {
for (std::size_t i = 0; i < N; ++i) {
key[i] = k[i];
}
}
constexpr std::string_view key_view() const {
return {key, N - 1};
}
};
// Helper to create field step
template<std::size_t N>
consteval auto make_field_step(const char (&name)[N]) {
return path_step<N>(step_type::field, name, 0);
}
// Helper to create array index step
consteval auto make_index_step(std::size_t idx) {
return path_step<1>(step_type::array_index, "", idx);
}
// Parse state for compile-time JSON path parsing
struct parse_result {
bool success;
std::size_t pos;
std::string_view error_msg;
};
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
struct json_path_parser {
static constexpr std::string_view path_str = Path.view();
// Skip leading $ if present
static consteval std::size_t skip_root() {
if (!path_str.empty() && path_str[0] == '$') {
return 1;
}
return 0;
}
// Count the number of steps in the path at compile time
static consteval std::size_t count_steps() {
std::size_t count = 0;
std::size_t i = skip_root();
while (i < path_str.size()) {
if (path_str[i] == '.') {
// Field access: .field
++i;
if (i >= path_str.size()) break;
// Skip field name
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
++i;
}
++count;
} else if (path_str[i] == '[') {
// Array or bracket notation
++i;
if (i >= path_str.size()) break;
if (path_str[i] == '"' || path_str[i] == '\'') {
// Field access: ["field"] or ['field']
char quote = path_str[i];
++i;
while (i < path_str.size() && path_str[i] != quote) {
++i;
}
if (i < path_str.size()) ++i; // skip closing quote
if (i < path_str.size() && path_str[i] == ']') ++i;
} else {
// Array index: [0], [123]
while (i < path_str.size() && path_str[i] != ']') {
++i;
}
if (i < path_str.size()) ++i; // skip ]
}
++count;
} else {
++i;
}
}
return count;
}
// Parse a field name at compile time
static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
std::size_t len = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
out[len++] = path_str[i++];
}
out[len] = '\0';
return i;
}
// Parse an array index at compile time
static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
std::size_t index = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
index = index * 10 + (path_str[i] - '0');
++i;
}
return {i, index};
}
};
// Compile-time path accessor generator
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
using value = ::simdjson::icelake::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
// If T is a struct, validates the path at compile time
// If T is void, skips validation
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
// Validate path at compile time if T is a struct
if constexpr (std::is_class_v<T>) {
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
}
// Parse the path at compile time to build access steps
return access_impl<parser.skip_root()>(doc_or_val.get_value());
}
// Extract value at path directly into target with compile-time type validation
// Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
// Validate path exists in struct definition
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
// Get the type at the end of the path
constexpr auto final_type = get_final_type();
// Verify target type matches the field type
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
// All validation done at compile time - just navigate and extract
auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get the final type by walking the path through the struct type
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
// .field syntax
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) break;
if (path_view[i] == '"' || path_view[i] == '\'') {
// ["field"] syntax
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip quote
if (i < path_view.size() && path_view[i] == ']') ++i;
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else {
// [index] syntax - extract element type
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
current_type = get_element_type_reflected(current_type);
}
} else {
++i;
}
}
return current_type;
}
private:
// Walk path and extract directly into final field using compile-time reflection
template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
if (current.error()) return current.error();
// Base case: end of path - extract into target
if constexpr (PathPos >= path_view.size()) {
return current.get(target_ref);
}
// Field access: .field_name
else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
}
// Bracket notation: [index] or ["field"]
else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
constexpr auto elem_type = get_element_type_reflected(CurrentType);
static_assert(elem_type != ^^void, "Could not determine array element type");
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto elem_value = arr.at(index);
if constexpr (next_pos >= path_view.size()) {
return elem_value.get(target_ref);
} else {
return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
}
}
}
// Skip unexpected characters and continue
else {
return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
}
}
// Find member by name in reflected type
static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == name) {
return mem;
}
}
}
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if (current.error()) return current;
if constexpr (PathPos >= path_view.size()) {
return current;
} else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto next_value = arr.at(index);
return access_impl<next_pos>(next_value);
}
} else {
return access_impl<PathPos + 1>(current);
}
}
// Parse next field name
static consteval auto parse_next_field(std::size_t start) {
std::size_t i = start + 1;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
return std::make_tuple(field_name, i);
}
// Parse bracket notation: returns (is_field, field_name, next_pos, index)
static consteval auto parse_bracket(std::size_t start) {
std::size_t i = start + 1; // skip '['
if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
// Field access
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip closing quote
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(true, field_name, i, std::size_t(0));
} else {
// Array index
std::size_t index = 0;
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
index = index * 10 + (path_view[i] - '0');
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(false, std::string_view{}, i, index);
}
}
public:
// Check if reflected type is array-like (C-style array or indexable container)
// Uses reflection to test: 1) std::meta::is_array_type() for C arrays
// 2) std::meta::substitute() to test concepts::indexable_container concept
static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return true;
}
if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
}
return false;
}
// Extract element type from reflected array or container
// For C arrays: uses std::meta::remove_extent()
// For containers: finds value_type member using std::meta::members_of()
static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return std::meta::remove_extent(type_reflection);
}
auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::is_type(mem)) {
auto name = std::meta::identifier_of(mem);
if (name == "value_type") {
return mem;
}
}
}
return ^^void;
}
private:
// Check if type has member with given name
template<typename Type>
static consteval bool has_member(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return true;
}
}
return false;
}
// Get type of member by name
template<typename Type>
static consteval auto get_member_type(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return std::meta::type_of(mem);
}
}
return ^^void;
}
// Check if non-reflected type is array-like
template<typename Type>
static consteval bool is_container_type() {
using BaseType = std::remove_cvref_t<Type>;
if constexpr (requires { typename BaseType::value_type; }) {
return true;
}
if constexpr (std::is_array_v<BaseType>) {
return true;
}
return false;
}
// Extract element type from non-reflected container
template<typename Type>
using extract_element_type = std::conditional_t<
requires { typename std::remove_cvref_t<Type>::value_type; },
typename std::remove_cvref_t<Type>::value_type,
std::conditional_t<
std::is_array_v<std::remove_cvref_t<Type>>,
std::remove_extent_t<std::remove_cvref_t<Type>>,
void
>
>;
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) return false;
if (path_view[i] == '"' || path_view[i] == '\'') {
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i;
if (i < path_view.size() && path_view[i] == ']') ++i;
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else {
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
if (!is_array_like_reflected(current_type)) {
return false;
}
auto new_type = get_element_type_reflected(current_type);
if (new_type == ^^void) {
return false;
}
current_type = new_type;
}
} else {
++i;
}
}
return true;
}
};
// Compile-time path accessor with validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::icelake::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<T, Path>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::icelake::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<void, Path>;
return accessor::access(doc_or_val);
}
// ============================================================================
// JSON Pointer Compile-Time Support (RFC 6901)
// ============================================================================
// JSON Pointer parser: /field/0/nested (slash-separated)
template<constevalutil::fixed_string Pointer>
struct json_pointer_parser {
static constexpr std::string_view pointer_str = Pointer.view();
// Unescape token: ~0 -> ~, ~1 -> /
static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
out_len = 0;
for (std::size_t i = 0; i < src.size(); ++i) {
if (src[i] == '~' && i + 1 < src.size()) {
if (src[i + 1] == '0') {
dest[out_len++] = '~';
++i;
} else if (src[i + 1] == '1') {
dest[out_len++] = '/';
++i;
} else {
dest[out_len++] = src[i];
}
} else {
dest[out_len++] = src[i];
}
}
}
// Check if token is numeric
static consteval bool is_numeric(std::string_view token) {
if (token.empty()) return false;
if (token[0] == '0' && token.size() > 1) return false;
for (char c : token) {
if (c < '0' || c > '9') return false;
}
return true;
}
// Parse numeric token to index
static consteval std::size_t parse_index(std::string_view token) {
std::size_t result = 0;
for (char c : token) {
result = result * 10 + (c - '0');
}
return result;
}
// Count tokens in pointer
static consteval std::size_t count_tokens() {
if (pointer_str.empty() || pointer_str == "/") return 0;
std::size_t count = 0;
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
while (pos < pointer_str.size()) {
++count;
std::size_t next_slash = pointer_str.find('/', pos);
if (next_slash == std::string_view::npos) break;
pos = next_slash + 1;
}
return count;
}
// Get Nth token
static consteval std::string_view get_token(std::size_t token_index) {
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
std::size_t current_token = 0;
while (current_token < token_index) {
std::size_t next_slash = pointer_str.find('/', pos);
pos = next_slash + 1;
++current_token;
}
std::size_t token_end = pointer_str.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_str.size();
return pointer_str.substr(pos, token_end - pos);
}
};
// JSON Pointer accessor
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
using parser = json_pointer_parser<Pointer>;
static constexpr std::string_view pointer_view = Pointer.view();
static constexpr std::size_t token_count = parser::count_tokens();
// Validate pointer against struct definition
static consteval bool validate_pointer() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
// Extract token up to next /
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
return false;
}
current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
} else {
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) return false;
}
pos = token_end + 1;
}
return true;
}
// Recursive accessor
template<std::size_t TokenIndex>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if constexpr (TokenIndex >= token_count) {
return current;
} else {
constexpr std::string_view token = parser::get_token(TokenIndex);
if constexpr (parser::is_numeric(token)) {
constexpr std::size_t index = parser::parse_index(token);
auto arr = current.get_array().value_unsafe();
auto next_value = arr.at(index);
return access_impl<TokenIndex + 1>(next_value);
} else {
auto obj = current.get_object().value_unsafe();
auto next_value = obj.find_field_unordered(token);
return access_impl<TokenIndex + 1>(next_value);
}
}
}
// Access JSON value at pointer
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
if constexpr (std::is_class_v<T>) {
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
}
if (pointer_view.empty() || pointer_view == "/") {
if constexpr (requires { doc_or_val.get_value(); }) {
return doc_or_val.get_value();
} else {
return doc_or_val;
}
}
simdjson_result<value> current = doc_or_val.get_value();
return access_impl<0>(current);
}
// Extract value at pointer directly into target with type validation
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
constexpr auto final_type = get_final_type();
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
simdjson_result<value> current_value = doc_or_val.get_value();
auto json_value = access_impl<0>(current_value);
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get final type by walking pointer through struct
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
} else {
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
break;
}
}
}
pos = token_end + 1;
}
return current_type;
}
};
// Compile-time JSON Pointer accessor with validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::icelake::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<T, Pointer>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::icelake::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<void, Pointer>;
return accessor::access(doc_or_val);
}
} // namespace json_path
} // namespace ondemand
} // namespace icelake
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* end file simdjson/generic/ondemand/compile_time_accessors.h for icelake */
/* end file simdjson/generic/ondemand/amalgamated.h for icelake */
/* including simdjson/icelake/end.h: #include "simdjson/icelake/end.h" */
/* begin file simdjson/icelake/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/icelake/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_ICELAKE
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "icelake" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/icelake/end.h */
#endif // SIMDJSON_ICELAKE_ONDEMAND_H
/* end file simdjson/icelake/ondemand.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(ppc64)
/* including simdjson/ppc64/ondemand.h: #include "simdjson/ppc64/ondemand.h" */
/* begin file simdjson/ppc64/ondemand.h */
#ifndef SIMDJSON_PPC64_ONDEMAND_H
#define SIMDJSON_PPC64_ONDEMAND_H
/* including simdjson/ppc64/begin.h: #include "simdjson/ppc64/begin.h" */
/* begin file simdjson/ppc64/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "ppc64" */
#define SIMDJSON_IMPLEMENTATION ppc64
/* including simdjson/ppc64/base.h: #include "simdjson/ppc64/base.h" */
/* begin file simdjson/ppc64/base.h */
#ifndef SIMDJSON_PPC64_BASE_H
#define SIMDJSON_PPC64_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for ALTIVEC (PPC64).
*/
namespace ppc64 {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_BASE_H
/* end file simdjson/ppc64/base.h */
/* including simdjson/ppc64/intrinsics.h: #include "simdjson/ppc64/intrinsics.h" */
/* begin file simdjson/ppc64/intrinsics.h */
#ifndef SIMDJSON_PPC64_INTRINSICS_H
#define SIMDJSON_PPC64_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// This should be the correct header whether
// you use visual studio or other compilers.
#include <altivec.h>
// These are defined by altivec.h in GCC toolchain, it is safe to undef them.
#ifdef bool
#undef bool
#endif
#ifdef vector
#undef vector
#endif
static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "insufficient padding for ppc64");
#endif // SIMDJSON_PPC64_INTRINSICS_H
/* end file simdjson/ppc64/intrinsics.h */
/* including simdjson/ppc64/bitmanipulation.h: #include "simdjson/ppc64/bitmanipulation.h" */
/* begin file simdjson/ppc64/bitmanipulation.h */
#ifndef SIMDJSON_PPC64_BITMANIPULATION_H
#define SIMDJSON_PPC64_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num - 1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline int count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num); // Visual Studio wants two underscores
}
#else
simdjson_inline int count_ones(uint64_t input_num) {
return __builtin_popcountll(input_num);
}
#endif
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_BITMANIPULATION_H
/* end file simdjson/ppc64/bitmanipulation.h */
/* including simdjson/ppc64/bitmask.h: #include "simdjson/ppc64/bitmask.h" */
/* begin file simdjson/ppc64/bitmask.h */
#ifndef SIMDJSON_PPC64_BITMASK_H
#define SIMDJSON_PPC64_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is
// encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
// You can use the version below, however gcc sometimes miscompiles
// vec_pmsum_be, it happens somewhere around between 8 and 9th version.
// The performance boost was not noticeable, falling back to a usual
// implementation.
// __vector unsigned long long all_ones = {~0ull, ~0ull};
// __vector unsigned long long mask = {bitmask, 0};
// // Clang and GCC return different values for pmsum for ull so cast it to one.
// // Generally it is not specified by ALTIVEC ISA what is returned by
// // vec_pmsum_be.
// #if defined(__LITTLE_ENDIAN__)
// return (uint64_t)(((__vector unsigned long long)vec_pmsum_be(all_ones, mask))[0]);
// #else
// return (uint64_t)(((__vector unsigned long long)vec_pmsum_be(all_ones, mask))[1]);
// #endif
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif
/* end file simdjson/ppc64/bitmask.h */
/* including simdjson/ppc64/numberparsing_defs.h: #include "simdjson/ppc64/numberparsing_defs.h" */
/* begin file simdjson/ppc64/numberparsing_defs.h */
#ifndef SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
#define SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#if defined(__linux__)
#include <byteswap.h>
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#endif
namespace simdjson {
namespace ppc64 {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
#ifdef __BIG_ENDIAN__
#if defined(__linux__)
val = bswap_64(val);
#elif defined(__FreeBSD__)
val = bswap64(val);
#endif
#endif
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace ppc64
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H
/* end file simdjson/ppc64/numberparsing_defs.h */
/* including simdjson/ppc64/simd.h: #include "simdjson/ppc64/simd.h" */
/* begin file simdjson/ppc64/simd.h */
#ifndef SIMDJSON_PPC64_SIMD_H
#define SIMDJSON_PPC64_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace ppc64 {
namespace {
namespace simd {
using __m128i = __vector unsigned char;
template <typename Child> struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i &() const {
return this->value;
}
simdjson_inline operator __m128i &() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const {
return vec_or(this->value, (__m128i)other);
}
simdjson_inline Child operator&(const Child other) const {
return vec_and(this->value, (__m128i)other);
}
simdjson_inline Child operator^(const Child other) const {
return vec_xor(this->value, (__m128i)other);
}
simdjson_inline Child bit_andnot(const Child other) const {
return vec_andc(this->value, (__m128i)other);
}
simdjson_inline Child &operator|=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast | other;
return *this_cast;
}
simdjson_inline Child &operator&=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast & other;
return *this_cast;
}
simdjson_inline Child &operator^=(const Child other) {
auto this_cast = static_cast<Child*>(this);
*this_cast = *this_cast ^ other;
return *this_cast;
}
};
template <typename T, typename Mask = simd8<bool>>
struct base8 : base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) {
return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs);
}
static const int SIZE = sizeof(base<simd8<T>>::value);
template <int N = 1>
simdjson_inline simd8<T> prev(simd8<T> prev_chunk) const {
__m128i chunk = this->value;
#ifdef __LITTLE_ENDIAN__
chunk = (__m128i)vec_reve(this->value);
prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk);
#endif
chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N);
#ifdef __LITTLE_ENDIAN__
chunk = (__m128i)vec_reve((__m128i)chunk);
#endif
return chunk;
}
};
// SIMD byte mask type (returned by things like eq and gt)
template <> struct simd8<bool> : base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) {
return (__m128i)vec_splats((unsigned char)(-(!!_value)));
}
simdjson_inline simd8() : base8<bool>() {}
simdjson_inline simd8(const __m128i _value)
: base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value)
: base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const {
__vector unsigned long long result;
const __m128i perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40,
0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x00};
result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value,
(__m128i)perm_mask));
#ifdef __LITTLE_ENDIAN__
return static_cast<int>(result[1]);
#else
return static_cast<int>(result[0]);
#endif
}
simdjson_inline bool any() const {
return !vec_all_eq(this->value, (__m128i)vec_splats(0));
}
simdjson_inline simd8<bool> operator~() const {
return this->value ^ (__m128i)splat(true);
}
};
template <typename T> struct base8_numeric : base8<T> {
static simdjson_inline simd8<T> splat(T value) {
(void)value;
return (__m128i)vec_splats(value);
}
static simdjson_inline simd8<T> zero() { return splat(0); }
static simdjson_inline simd8<T> load(const T values[16]) {
return (__m128i)(vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(values)));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(T v0, T v1, T v2, T v3, T v4,
T v5, T v6, T v7, T v8, T v9,
T v10, T v11, T v12, T v13,
T v14, T v15) {
return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
v14, v15);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value)
: base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const {
vec_vsx_st(this->value, 0, reinterpret_cast<__m128i *>(dst));
}
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const {
return (__m128i)((__m128i)this->value + (__m128i)other);
}
simdjson_inline simd8<T> operator-(const simd8<T> other) const {
return (__m128i)((__m128i)this->value - (__m128i)other);
}
simdjson_inline simd8<T> &operator+=(const simd8<T> other) {
*this = *this + other;
return *static_cast<simd8<T> *>(this);
}
simdjson_inline simd8<T> &operator-=(const simd8<T> other) {
*this = *this - other;
return *static_cast<simd8<T> *>(this);
}
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior
// for out of range values)
template <typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted
// as a bitset). Passing a 0 value for mask would be equivalent to writing out
// every byte to output. Only the first 16 - count_ones(mask) bytes of the
// result are significant but 16 bytes get written. Design consideration: it
// seems like a function with the signature simd8<L> compress(uint32_t mask)
// would be sensible, but the AVX ISA makes this kind of approach difficult.
template <typename L>
simdjson_inline void compress(uint16_t mask, L *output) const {
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
using internal::thintable_epi8;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
#ifdef __LITTLE_ENDIAN__
__m128i shufmask = (__m128i)(__vector unsigned long long){
thintable_epi8[mask1], thintable_epi8[mask2]};
#else
__m128i shufmask = (__m128i)(__vector unsigned long long){
thintable_epi8[mask2], thintable_epi8[mask1]};
shufmask = (__m128i)vec_reve((__m128i)shufmask);
#endif
// we increment by 0x08 the second half of the mask
shufmask = ((__m128i)shufmask) +
((__m128i)(__vector int){0, 0, 0x08080808, 0x08080808});
// this is the version "nearly pruned"
__m128i pruned = vec_perm(this->value, this->value, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(pshufb_combine_table + pop1 * 8));
__m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask);
vec_vsx_st(answer, 0, reinterpret_cast<__m128i *>(output));
}
template <typename L>
simdjson_inline simd8<L>
lookup_16(L replace0, L replace1, L replace2, L replace3, L replace4,
L replace5, L replace6, L replace7, L replace8, L replace9,
L replace10, L replace11, L replace12, L replace13, L replace14,
L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3, replace4, replace5, replace6,
replace7, replace8, replace9, replace10, replace11, replace12,
replace13, replace14, replace15));
}
};
// Signed bytes
template <> struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value)
: base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t *values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(int8_t v0, int8_t v1, int8_t v2, int8_t v3,
int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11,
int8_t v12, int8_t v13, int8_t v14, int8_t v15)
: simd8((__m128i)(__vector signed char){v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10, v11, v12, v13, v14,
v15}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t>
repeat_16(int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5,
int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11,
int8_t v12, int8_t v13, int8_t v14, int8_t v15) {
return simd8<int8_t>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t>
max_val(const simd8<int8_t> other) const {
return (__m128i)vec_max((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<int8_t>
min_val(const simd8<int8_t> other) const {
return (__m128i)vec_min((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<bool>
operator>(const simd8<int8_t> other) const {
return (__m128i)vec_cmpgt((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
simdjson_inline simd8<bool>
operator<(const simd8<int8_t> other) const {
return (__m128i)vec_cmplt((__vector signed char)this->value,
(__vector signed char)(__m128i)other);
}
};
// Unsigned bytes
template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value)
: base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t *values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline
simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5,
uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10,
uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15)
: simd8((__m128i){v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t>
repeat_16(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4,
uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9,
uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14,
uint8_t v15) {
return simd8<uint8_t>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
v13, v14, v15);
}
// Saturated math
simdjson_inline simd8<uint8_t>
saturating_add(const simd8<uint8_t> other) const {
return (__m128i)vec_adds(this->value, (__m128i)other);
}
simdjson_inline simd8<uint8_t>
saturating_sub(const simd8<uint8_t> other) const {
return (__m128i)vec_subs(this->value, (__m128i)other);
}
// Order-specific operations
simdjson_inline simd8<uint8_t>
max_val(const simd8<uint8_t> other) const {
return (__m128i)vec_max(this->value, (__m128i)other);
}
simdjson_inline simd8<uint8_t>
min_val(const simd8<uint8_t> other) const {
return (__m128i)vec_min(this->value, (__m128i)other);
}
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t>
gt_bits(const simd8<uint8_t> other) const {
return this->saturating_sub(other);
}
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t>
lt_bits(const simd8<uint8_t> other) const {
return other.saturating_sub(*this);
}
simdjson_inline simd8<bool>
operator<=(const simd8<uint8_t> other) const {
return other.max_val(*this) == other;
}
simdjson_inline simd8<bool>
operator>=(const simd8<uint8_t> other) const {
return other.min_val(*this) == other;
}
simdjson_inline simd8<bool>
operator>(const simd8<uint8_t> other) const {
return this->gt_bits(other).any_bits_set();
}
simdjson_inline simd8<bool>
operator<(const simd8<uint8_t> other) const {
return this->gt_bits(other).any_bits_set();
}
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const {
return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0)));
}
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const {
return (*this & bits).bits_not_set();
}
simdjson_inline simd8<bool> any_bits_set() const {
return ~this->bits_not_set();
}
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const {
return ~this->bits_not_set(bits);
}
simdjson_inline bool bits_not_set_anywhere() const {
return vec_all_eq(this->value, (__m128i)vec_splats(0));
}
simdjson_inline bool any_bits_set_anywhere() const {
return !bits_not_set_anywhere();
}
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
return vec_all_eq(vec_and(this->value, (__m128i)bits),
(__m128i)vec_splats(0));
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const {
return !bits_not_set_anywhere(bits);
}
template <int N> simdjson_inline simd8<uint8_t> shr() const {
return simd8<uint8_t>(
(__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N)));
}
template <int N> simdjson_inline simd8<uint8_t> shl() const {
return simd8<uint8_t>(
(__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N)));
}
};
template <typename T> struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4,
"PPC64 kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T> &o) = delete; // no copy allowed
simd8x64<T> &
operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1,
const simd8<T> chunk2, const simd8<T> chunk3)
: chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64])
: chunks{simd8<T>::load(ptr), simd8<T>::load(ptr + 16),
simd8<T>::load(ptr + 32), simd8<T>::load(ptr + 48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr + sizeof(simd8<T>) * 0);
this->chunks[1].store(ptr + sizeof(simd8<T>) * 1);
this->chunks[2].store(ptr + sizeof(simd8<T>) * 2);
this->chunks[3].store(ptr + sizeof(simd8<T>) * 3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) |
(this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T *output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16),
output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32),
output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48),
output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
uint64_t r1 = this->chunks[1].to_bitmask();
uint64_t r2 = this->chunks[2].to_bitmask();
uint64_t r3 = this->chunks[3].to_bitmask();
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(this->chunks[0] == mask, this->chunks[1] == mask,
this->chunks[2] == mask, this->chunks[3] == mask)
.to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3])
.to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(this->chunks[0] <= mask, this->chunks[1] <= mask,
this->chunks[2] <= mask, this->chunks[3] <= mask)
.to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_SIMD_INPUT_H
/* end file simdjson/ppc64/simd.h */
/* including simdjson/ppc64/stringparsing_defs.h: #include "simdjson/ppc64/stringparsing_defs.h" */
/* begin file simdjson/ppc64/stringparsing_defs.h */
#ifndef SIMDJSON_PPC64_STRINGPARSING_DEFS_H
#define SIMDJSON_PPC64_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/simd.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote
copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() {
return ((bs_bits - 1) & quote_bits) != 0;
}
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() {
return trailing_zeroes(quote_bits);
}
simdjson_inline int backslash_index() {
return trailing_zeroes(bs_bits);
}
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote
backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1),
"backslash and quote finder must process fewer than "
"SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on
// PPC; therefore, we smash them together into a 64-byte mask and get the
// bitmask from there.
uint64_t bs_and_quote =
simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
// We store it as a 64-bit bitmask even though we only need 16 bits.
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_PPC64_STRINGPARSING_DEFS_H
/* end file simdjson/ppc64/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/ppc64/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for ppc64: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for ppc64 */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for ppc64: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::ppc64::number_type */
using number_type = simdjson::ppc64::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for ppc64 */
/* including simdjson/generic/ondemand/deserialize.h for ppc64: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for ppc64 */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<ppc64::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<ppc64::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<ppc64::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<ppc64::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = ppc64::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = ppc64::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = ppc64::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = ppc64::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = ppc64::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = ppc64::ondemand::array;
using object_type = ppc64::ondemand::object;
using value_type = ppc64::ondemand::value;
using document_type = ppc64::ondemand::document;
using document_reference_type = ppc64::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for ppc64 */
/* including simdjson/generic/ondemand/value_iterator.h for ppc64: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::value_iterator> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for ppc64 */
/* including simdjson/generic/ondemand/value.h for ppc64: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::value> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::value> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<ppc64::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator ppc64::ondemand::array() noexcept(false);
simdjson_inline operator ppc64::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator ppc64::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<ppc64::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<ppc64::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<ppc64::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<ppc64::ondemand::value>::get<ppc64::ondemand::value>(
ppc64::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<ppc64::ondemand::value>
simdjson_result<ppc64::ondemand::value>::get<ppc64::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for ppc64 */
/* including simdjson/generic/ondemand/logger.h for ppc64: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for ppc64 */
/* including simdjson/generic/ondemand/token_iterator.h for ppc64: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::token_iterator> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for ppc64 */
/* including simdjson/generic/ondemand/json_iterator.h for ppc64: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::json_iterator> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for ppc64 */
/* including simdjson/generic/ondemand/json_type.h for ppc64: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::json_type> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::json_type> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for ppc64 */
/* including simdjson/generic/ondemand/raw_json_string.h for ppc64: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::raw_json_string> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(ppc64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(ppc64::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for ppc64 */
/* including simdjson/generic/ondemand/parser.h for ppc64: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::parser> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::parser> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for ppc64 */
// All other declarations
/* including simdjson/generic/ondemand/array.h for ppc64: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::array> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::array> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, ppc64::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, ppc64::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, ppc64::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, ppc64::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for ppc64 */
/* including simdjson/generic/ondemand/array_iterator.h for ppc64: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::array_iterator> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<ppc64::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(ppc64::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<ppc64::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<ppc64::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<ppc64::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for ppc64 */
/* including simdjson/generic/ondemand/document.h for ppc64: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::document> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::document> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using ppc64::implementation_simdjson_result_base<ppc64::ondemand::document>::operator*;
using ppc64::implementation_simdjson_result_base<ppc64::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, ppc64::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator ppc64::ondemand::array() & noexcept(false);
simdjson_inline operator ppc64::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator ppc64::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator ppc64::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<ppc64::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<ppc64::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::document_reference> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator ppc64::ondemand::array() & noexcept(false);
simdjson_inline operator ppc64::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator ppc64::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator ppc64::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<ppc64::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<ppc64::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for ppc64 */
/* including simdjson/generic/ondemand/document_stream.h for ppc64: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace ppc64 {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::document_stream> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for ppc64 */
/* including simdjson/generic/ondemand/field.h for ppc64: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::field> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::field> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<ppc64::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for ppc64 */
/* including simdjson/generic/ondemand/key_selector.h for ppc64: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace ppc64 {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for ppc64 */
/* including simdjson/generic/ondemand/object.h for ppc64: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::object> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::object> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<ppc64::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<ppc64::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(ppc64::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, ppc64::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, ppc64::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, ppc64::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, ppc64::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires ppc64::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, ppc64::ondemand::value>
simdjson_inline ppc64::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, ppc64::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires ppc64::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(ppc64::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline ppc64::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(ppc64::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(ppc64::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline ppc64::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(ppc64::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for ppc64 */
/* including simdjson/generic/ondemand/object_iterator.h for ppc64: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<ppc64::ondemand::object_iterator> : public ppc64::implementation_simdjson_result_base<ppc64::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(ppc64::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<ppc64::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<ppc64::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<ppc64::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for ppc64 */
/* including simdjson/generic/ondemand/ranges.h for ppc64: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::ppc64::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::ppc64::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for ppc64 */
/* including simdjson/generic/ondemand/serialization.h for ppc64: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, ppc64::ondemand::document> &&
!std::same_as<T, ppc64::ondemand::value> &&
!std::same_as<T, ppc64::ondemand::object> &&
!std::same_as<T, ppc64::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace ppc64 { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::object> x);
#endif
}}} // namespace simdjson::ppc64::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for ppc64 */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for ppc64: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for ppc64 */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
ppc64::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, ppc64::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
ppc64::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, ppc64::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
ppc64::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, ppc64::ondemand::value &val, T &out) noexcept(false) {
ppc64::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, ppc64::ondemand::document &doc, T &out) noexcept(false) {
ppc64::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, ppc64::ondemand::document_reference &doc, T &out) noexcept(false) {
ppc64::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, ppc64::ondemand::number>
&& !std::is_same_v<T, ppc64::ondemand::document>
&& !std::is_same_v<T, ppc64::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^ppc64::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = ppc64::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, ppc64::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
ppc64::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
ppc64::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
ppc64::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
ppc64::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, ppc64::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
ppc64::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, ppc64::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, ppc64::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, ppc64::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for ppc64 */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for ppc64: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::array>::simdjson_result(
ppc64::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::array>(
std::forward<ppc64::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<ppc64::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<ppc64::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for ppc64 */
/* including simdjson/generic/ondemand/array_iterator-inl.h for ppc64: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator>::simdjson_result(
ppc64::ondemand::array_iterator &&value
) noexcept
: ppc64::implementation_simdjson_result_base<ppc64::ondemand::array_iterator>(std::forward<ppc64::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: ppc64::implementation_simdjson_result_base<ppc64::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<ppc64::ondemand::array_iterator>::operator==(const simdjson_result<ppc64::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<ppc64::ondemand::array_iterator>::operator!=(const simdjson_result<ppc64::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> &simdjson_result<ppc64::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<ppc64::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for ppc64 */
/* including simdjson/generic/ondemand/value-inl.h for ppc64: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::value>::simdjson_result(
ppc64::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<ppc64::ondemand::value>(
std::forward<ppc64::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<ppc64::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<ppc64::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<ppc64::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<ppc64::ondemand::array> simdjson_result<ppc64::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<ppc64::ondemand::object> simdjson_result<ppc64::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<ppc64::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<ppc64::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<ppc64::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<ppc64::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<ppc64::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<ppc64::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<ppc64::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<ppc64::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<ppc64::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<ppc64::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<ppc64::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<ppc64::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<ppc64::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<ppc64::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<ppc64::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<ppc64::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<ppc64::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<ppc64::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> simdjson_result<ppc64::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<ppc64::ondemand::value>::get<ppc64::ondemand::value>(ppc64::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<ppc64::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<ppc64::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::get<ppc64::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<ppc64::ondemand::json_type> simdjson_result<ppc64::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<ppc64::number_type> simdjson_result<ppc64::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<ppc64::ondemand::number> simdjson_result<ppc64::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator ppc64::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator ppc64::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator ppc64::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<ppc64::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<ppc64::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<ppc64::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for ppc64 */
/* including simdjson/generic/ondemand/document-inl.h for ppc64: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace ppc64 {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::document>::simdjson_result(
ppc64::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<ppc64::ondemand::document>(
std::forward<ppc64::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<ppc64::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<ppc64::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<ppc64::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<ppc64::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::array> simdjson_result<ppc64::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<ppc64::ondemand::object> simdjson_result<ppc64::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<ppc64::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<ppc64::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<ppc64::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<ppc64::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<ppc64::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<ppc64::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<ppc64::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<ppc64::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<ppc64::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<ppc64::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<ppc64::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<ppc64::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<ppc64::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<ppc64::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<ppc64::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<ppc64::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<ppc64::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> simdjson_result<ppc64::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<ppc64::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<ppc64::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<ppc64::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<ppc64::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<ppc64::ondemand::document> simdjson_result<ppc64::ondemand::document>::get<ppc64::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<ppc64::ondemand::document> simdjson_result<ppc64::ondemand::document>::get<ppc64::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<ppc64::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document>::get<ppc64::ondemand::document>(ppc64::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document>::get<ppc64::ondemand::document>(ppc64::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<ppc64::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<ppc64::ondemand::json_type> simdjson_result<ppc64::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<ppc64::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<ppc64::number_type> simdjson_result<ppc64::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<ppc64::ondemand::number> simdjson_result<ppc64::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, ppc64::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator ppc64::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator ppc64::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator ppc64::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<ppc64::ondemand::document>::operator ppc64::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<ppc64::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<ppc64::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<ppc64::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<ppc64::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace ppc64 {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::simdjson_result(ppc64::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<ppc64::ondemand::document_reference>(std::forward<ppc64::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<ppc64::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<ppc64::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<ppc64::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<ppc64::ondemand::array_iterator> simdjson_result<ppc64::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::array> simdjson_result<ppc64::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<ppc64::ondemand::object> simdjson_result<ppc64::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<ppc64::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<ppc64::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<ppc64::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<ppc64::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<ppc64::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<ppc64::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<ppc64::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<ppc64::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<ppc64::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<ppc64::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<ppc64::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<ppc64::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<ppc64::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<ppc64::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<ppc64::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<ppc64::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<ppc64::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> simdjson_result<ppc64::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<ppc64::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<ppc64::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<ppc64::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, ppc64::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<ppc64::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<ppc64::ondemand::json_type> simdjson_result<ppc64::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document_reference>::get(ppc64::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document_reference>::get(ppc64::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<ppc64::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<ppc64::number_type> simdjson_result<ppc64::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<ppc64::ondemand::number> simdjson_result<ppc64::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, ppc64::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, ppc64::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator ppc64::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator ppc64::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator ppc64::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<ppc64::ondemand::document_reference>::operator ppc64::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<ppc64::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<ppc64::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for ppc64 */
/* including simdjson/generic/ondemand/document_stream-inl.h for ppc64: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace ppc64 {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<ppc64::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::document_stream>::simdjson_result(
ppc64::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<ppc64::ondemand::document_stream>(
std::forward<ppc64::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for ppc64 */
/* including simdjson/generic/ondemand/field-inl.h for ppc64: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::field>::simdjson_result(
ppc64::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<ppc64::ondemand::field>(
std::forward<ppc64::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<ppc64::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string> simdjson_result<ppc64::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<ppc64::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<ppc64::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<ppc64::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for ppc64 */
/* including simdjson/generic/ondemand/json_iterator-inl.h for ppc64: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::json_iterator>::simdjson_result(ppc64::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::json_iterator>(std::forward<ppc64::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<ppc64::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for ppc64 */
/* including simdjson/generic/ondemand/json_type-inl.h for ppc64: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::json_type>::simdjson_result(ppc64::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::json_type>(std::forward<ppc64::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<ppc64::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for ppc64 */
/* including simdjson/generic/ondemand/logger-inl.h for ppc64: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace ppc64 {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for ppc64 */
/* including simdjson/generic/ondemand/object-inl.h for ppc64: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::object>::simdjson_result(ppc64::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::object>(std::forward<ppc64::ondemand::object>(value)) {}
simdjson_inline simdjson_result<ppc64::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::object>(error) {}
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator> simdjson_result<ppc64::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator> simdjson_result<ppc64::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator> simdjson_result<ppc64::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<ppc64::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<ppc64::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<ppc64::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<ppc64::ondemand::value> simdjson_result<ppc64::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, ppc64::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<ppc64::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires ppc64::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, ppc64::ondemand::value>
simdjson_inline ppc64::ondemand::for_each_result
simdjson_result<ppc64::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, ppc64::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires ppc64::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(ppc64::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline ppc64::ondemand::for_each_result
simdjson_result<ppc64::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(ppc64::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(ppc64::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline ppc64::ondemand::for_each_result
simdjson_result<ppc64::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(ppc64::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<ppc64::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<ppc64::ondemand::object_position> simdjson_result<ppc64::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<ppc64::ondemand::object>::revert_position(ppc64::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<ppc64::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<ppc64::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for ppc64 */
/* including simdjson/generic/ondemand/object_iterator-inl.h for ppc64: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator>::simdjson_result(
ppc64::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::object_iterator>(std::forward<ppc64::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<ppc64::ondemand::field> simdjson_result<ppc64::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<ppc64::ondemand::object_iterator>::operator==(const simdjson_result<ppc64::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<ppc64::ondemand::object_iterator>::operator!=(const simdjson_result<ppc64::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<ppc64::ondemand::object_iterator> &simdjson_result<ppc64::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for ppc64 */
/* including simdjson/generic/ondemand/ranges-inl.h for ppc64: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace ppc64 {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::ppc64::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::ppc64::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::ppc64::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::ppc64::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::ppc64::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::ppc64::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for ppc64 */
/* including simdjson/generic/ondemand/parser-inl.h for ppc64: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::parser>::simdjson_result(ppc64::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::parser>(std::forward<ppc64::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<ppc64::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for ppc64 */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for ppc64: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string>::simdjson_result(ppc64::ondemand::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::raw_json_string>(std::forward<ppc64::ondemand::raw_json_string>(value)) {}
simdjson_inline simdjson_result<ppc64::ondemand::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<ppc64::ondemand::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline char simdjson_result<ppc64::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
if (error()) { return error(); }
return first[i];
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::raw_json_string>::unescape(ppc64::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<ppc64::ondemand::raw_json_string>::unescape_wobbly(ppc64::ondemand::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* end file simdjson/generic/ondemand/raw_json_string-inl.h for ppc64 */
/* including simdjson/generic/ondemand/token_iterator-inl.h for ppc64: #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/token_iterator-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::token_iterator>::simdjson_result(ppc64::ondemand::token_iterator &&value) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::token_iterator>(std::forward<ppc64::ondemand::token_iterator>(value)) {}
simdjson_inline simdjson_result<ppc64::ondemand::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/token_iterator-inl.h for ppc64 */
/* including simdjson/generic/ondemand/value_iterator-inl.h for ppc64: #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/value_iterator-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace ppc64 {
namespace ondemand {
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
bool escaping{false};
for (uint32_t i = 1; i < max_len; i++) {
switch (json[i]) {
case '"':
if (!escaping) { return true; }
escaping = false;
break;
case '\\':
escaping = !escaping;
break;
default:
escaping = false;
break;
}
}
return false;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
SIMDJSON_TRY(end_container());
return false;
}
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
// call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != '}') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
// If the last character is } *and* the first gibberish character is also '}'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed object.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
auto error = check_root_object();
if(error) { return error; }
return started_object();
}
simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
#if SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
return false;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return false;
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
// If someone queried a key but they not did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
//
// ```
// // At the beginning of a field
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // At the end of the object
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// ```
//
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
SIMDJSON_TRY(reset_object().get(has_value));
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
return raw_json_string(key);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
assert_at_next();
const uint8_t *k = _json_iter->return_current_and_advance();
if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
// After return_current_and_advance(), the current token is the ':' that follows
// the key. The closing quote sits just before it (only JSON whitespace may
// intervene), so step back from the ':' to the closing quote to get the length.
// In minified JSON this is a single back-step.
const char *q = reinterpret_cast<const char *>(_json_iter->peek());
do { --q; } while (*q != '"');
key = raw_json_string(k);
len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
assert_at_next();
if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
answer += std::string(" ]");
return answer;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
SIMDJSON_TRY( end_container() );
return false;
}
_json_iter->descend_to(depth()+1);
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
// also call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != ']') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
// If the last character is ] *and* the first gibberish character is also ']'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed array.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
auto error = check_root_array();
if (error) { return error; }
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
}
simdjson_inline bool value_iterator::is_root_negative() noexcept {
return numberparsing::is_negative(peek_root_scalar("numbersign"));
}
simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
return numberparsing::is_integer(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
return numberparsing::get_number_type(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
number num;
error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
if(error) { return error; }
return num;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<ppc64::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_inline value_iterator value_iterator::child() const noexcept {
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool value_iterator::at_end() const noexcept {
return _json_iter->at_end();
}
simdjson_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.position() == start_position();
}
simdjson_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
return _json_iter->error;
}
simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
return _json_iter->string_buf_loc();
}
simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
return *_json_iter;
}
simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
return *_json_iter;
}
simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(start_position());
}
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
logger::log_start_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
const uint8_t *json;
if (!is_at_start()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
return SUCCESS;
}
simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
return _json_iter->peek();
}
simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
logger::log_error(*_json_iter, start_position(), depth(), message);
return INCORRECT_TYPE;
}
simdjson_inline bool value_iterator::is_at_start() const noexcept {
return position() == start_position();
}
simdjson_inline bool value_iterator::is_at_key() const noexcept {
// Keys are at the same depth as the object.
// Note here that we could be safer and check that we are within an object,
// but we do not.
//
// As long as we are at the object's depth, in a valid document,
// we will only ever be at { , : or the actual string key: ".
return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
}
simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = position() - start_position();
return delta == 1 || delta == 2;
}
inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_inline void value_iterator::move_at_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position);
}
simdjson_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
// Unlike reenter_child(), this does not require the live depth to be
// exactly one level shallower than depth, nor does it validate against
// the parser's per-depth container-start bookkeeping: neither holds in
// general for a caller-supplied snapshot (see object_position). What
// must still always hold, regardless of what was captured or how far
// the live iterator has since moved, is that position and depth are
// themselves sane values -- this is the same bound reenter_child()
// itself applies unconditionally.
SIMDJSON_ASSUME(position != nullptr);
SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
_json_iter->_depth = depth;
_json_iter->token.set_position(position);
}
simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_array();
}
simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_object();
}
inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return json_type::unknown;
}
}
simdjson_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<ppc64::ondemand::value_iterator>::simdjson_result(ppc64::ondemand::value_iterator &&value) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::value_iterator>(std::forward<ppc64::ondemand::value_iterator>(value)) {}
simdjson_inline simdjson_result<ppc64::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<ppc64::ondemand::value_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/value_iterator-inl.h for ppc64 */
/* including simdjson/generic/ondemand/serialization-inl.h for ppc64: #include "simdjson/generic/ondemand/serialization-inl.h" */
/* begin file simdjson/generic/ondemand/serialization-inl.h for ppc64 */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-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/serialization.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace ppc64::ondemand;
ppc64::ondemand::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
ppc64::ondemand::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
ppc64::ondemand::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(ppc64::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<ppc64::ondemand::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace ppc64 { namespace ondemand {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::ppc64::ondemand::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::ppc64::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::ppc64::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* end file simdjson/generic/ondemand/serialization-inl.h for ppc64 */
// JSON path accessor (compile-time) - must be after inline definitions
/* including simdjson/generic/ondemand/compile_time_accessors.h for ppc64: #include "simdjson/generic/ondemand/compile_time_accessors.h" */
/* begin file simdjson/generic/ondemand/compile_time_accessors.h for ppc64 */
/**
* Compile-time JSON Path and JSON Pointer accessors using C++26 reflection (P2996)
*
* This file validates JSON paths/pointers against struct definitions at compile time
* and generates optimized accessor code with zero runtime overhead.
*
* ## How It Works
*
* **Compile Time**: Path is parsed, validated against struct, types are checked
* **Runtime**: Direct navigation with no parsing or validation overhead
*
* Example:
* ```cpp
* struct User { std::string name; std::vector<std::string> emails; };
*
* std::string email;
* path_accessor<User, ".emails[0]">::extract_field(doc, email);
*
* // Compile time validates:
* // 1. User has "emails" field
* // 2. "emails" is array-like
* // 3. Element type is std::string
* // 4. static_assert(^^std::string == ^^std::string)
*
* // Runtime just navigates:
* // doc.get_object().find_field("emails").get_array().at(0).get(email)
* ```
*
* ## Key Reflection APIs
*
* - `^^Type`: Reflect operator, converts type to std::meta::info
* - `std::meta::nonstatic_data_members_of(type)`: Get all fields of a struct
* - `std::meta::identifier_of(member)`: Get field name as string_view
* - `std::meta::type_of(member)`: Get reflected type of a field
* - `std::meta::is_array_type(type)`: Check if C-style array
* - `std::meta::remove_extent(array)`: Extract element type from array
* - `std::meta::members_of(type)`: Get all members including typedefs
* - `std::meta::is_type(member)`: Check if member is a type (vs field)
*
* All operations execute at compile time in consteval contexts.
*/
#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H */
/* amalgamation skipped (editor-only): */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
// is unlikely that we will have reflection support without concepts support.
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#include <string_view>
#include <cstddef>
#include <array>
namespace simdjson {
namespace ppc64 {
namespace ondemand {
/***
* JSONPath implementation for compile-time access
* RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
*/
namespace json_path {
// Note: value type must be fully defined before this header is included
// This is ensured by including this in amalgamated.h after value-inl.h
using ::simdjson::ppc64::ondemand::value;
// Path step types
enum class step_type {
field, // .field_name or ["field_name"]
array_index // [index]
};
// Represents a single step in a JSON path
template<std::size_t N>
struct path_step {
step_type type;
char key[N]; // Field name (empty for array indices)
std::size_t index; // Array index (0 for field access)
constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
: type(t), index(idx) {
for (std::size_t i = 0; i < N; ++i) {
key[i] = k[i];
}
}
constexpr std::string_view key_view() const {
return {key, N - 1};
}
};
// Helper to create field step
template<std::size_t N>
consteval auto make_field_step(const char (&name)[N]) {
return path_step<N>(step_type::field, name, 0);
}
// Helper to create array index step
consteval auto make_index_step(std::size_t idx) {
return path_step<1>(step_type::array_index, "", idx);
}
// Parse state for compile-time JSON path parsing
struct parse_result {
bool success;
std::size_t pos;
std::string_view error_msg;
};
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
struct json_path_parser {
static constexpr std::string_view path_str = Path.view();
// Skip leading $ if present
static consteval std::size_t skip_root() {
if (!path_str.empty() && path_str[0] == '$') {
return 1;
}
return 0;
}
// Count the number of steps in the path at compile time
static consteval std::size_t count_steps() {
std::size_t count = 0;
std::size_t i = skip_root();
while (i < path_str.size()) {
if (path_str[i] == '.') {
// Field access: .field
++i;
if (i >= path_str.size()) break;
// Skip field name
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
++i;
}
++count;
} else if (path_str[i] == '[') {
// Array or bracket notation
++i;
if (i >= path_str.size()) break;
if (path_str[i] == '"' || path_str[i] == '\'') {
// Field access: ["field"] or ['field']
char quote = path_str[i];
++i;
while (i < path_str.size() && path_str[i] != quote) {
++i;
}
if (i < path_str.size()) ++i; // skip closing quote
if (i < path_str.size() && path_str[i] == ']') ++i;
} else {
// Array index: [0], [123]
while (i < path_str.size() && path_str[i] != ']') {
++i;
}
if (i < path_str.size()) ++i; // skip ]
}
++count;
} else {
++i;
}
}
return count;
}
// Parse a field name at compile time
static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
std::size_t len = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
out[len++] = path_str[i++];
}
out[len] = '\0';
return i;
}
// Parse an array index at compile time
static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
std::size_t index = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
index = index * 10 + (path_str[i] - '0');
++i;
}
return {i, index};
}
};
// Compile-time path accessor generator
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
using value = ::simdjson::ppc64::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
// If T is a struct, validates the path at compile time
// If T is void, skips validation
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
// Validate path at compile time if T is a struct
if constexpr (std::is_class_v<T>) {
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
}
// Parse the path at compile time to build access steps
return access_impl<parser.skip_root()>(doc_or_val.get_value());
}
// Extract value at path directly into target with compile-time type validation
// Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
// Validate path exists in struct definition
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
// Get the type at the end of the path
constexpr auto final_type = get_final_type();
// Verify target type matches the field type
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
// All validation done at compile time - just navigate and extract
auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get the final type by walking the path through the struct type
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
// .field syntax
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) break;
if (path_view[i] == '"' || path_view[i] == '\'') {
// ["field"] syntax
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip quote
if (i < path_view.size() && path_view[i] == ']') ++i;
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else {
// [index] syntax - extract element type
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
current_type = get_element_type_reflected(current_type);
}
} else {
++i;
}
}
return current_type;
}
private:
// Walk path and extract directly into final field using compile-time reflection
template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
if (current.error()) return current.error();
// Base case: end of path - extract into target
if constexpr (PathPos >= path_view.size()) {
return current.get(target_ref);
}
// Field access: .field_name
else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
}
// Bracket notation: [index] or ["field"]
else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
constexpr auto elem_type = get_element_type_reflected(CurrentType);
static_assert(elem_type != ^^void, "Could not determine array element type");
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto elem_value = arr.at(index);
if constexpr (next_pos >= path_view.size()) {
return elem_value.get(target_ref);
} else {
return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
}
}
}
// Skip unexpected characters and continue
else {
return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
}
}
// Find member by name in reflected type
static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == name) {
return mem;
}
}
}
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if (current.error()) return current;
if constexpr (PathPos >= path_view.size()) {
return current;
} else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto next_value = arr.at(index);
return access_impl<next_pos>(next_value);
}
} else {
return access_impl<PathPos + 1>(current);
}
}
// Parse next field name
static consteval auto parse_next_field(std::size_t start) {
std::size_t i = start + 1;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
return std::make_tuple(field_name, i);
}
// Parse bracket notation: returns (is_field, field_name, next_pos, index)
static consteval auto parse_bracket(std::size_t start) {
std::size_t i = start + 1; // skip '['
if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
// Field access
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip closing quote
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(true, field_name, i, std::size_t(0));
} else {
// Array index
std::size_t index = 0;
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
index = index * 10 + (path_view[i] - '0');
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(false, std::string_view{}, i, index);
}
}
public:
// Check if reflected type is array-like (C-style array or indexable container)
// Uses reflection to test: 1) std::meta::is_array_type() for C arrays
// 2) std::meta::substitute() to test concepts::indexable_container concept
static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return true;
}
if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
}
return false;
}
// Extract element type from reflected array or container
// For C arrays: uses std::meta::remove_extent()
// For containers: finds value_type member using std::meta::members_of()
static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return std::meta::remove_extent(type_reflection);
}
auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::is_type(mem)) {
auto name = std::meta::identifier_of(mem);
if (name == "value_type") {
return mem;
}
}
}
return ^^void;
}
private:
// Check if type has member with given name
template<typename Type>
static consteval bool has_member(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return true;
}
}
return false;
}
// Get type of member by name
template<typename Type>
static consteval auto get_member_type(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return std::meta::type_of(mem);
}
}
return ^^void;
}
// Check if non-reflected type is array-like
template<typename Type>
static consteval bool is_container_type() {
using BaseType = std::remove_cvref_t<Type>;
if constexpr (requires { typename BaseType::value_type; }) {
return true;
}
if constexpr (std::is_array_v<BaseType>) {
return true;
}
return false;
}
// Extract element type from non-reflected container
template<typename Type>
using extract_element_type = std::conditional_t<
requires { typename std::remove_cvref_t<Type>::value_type; },
typename std::remove_cvref_t<Type>::value_type,
std::conditional_t<
std::is_array_v<std::remove_cvref_t<Type>>,
std::remove_extent_t<std::remove_cvref_t<Type>>,
void
>
>;
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) return false;
if (path_view[i] == '"' || path_view[i] == '\'') {
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i;
if (i < path_view.size() && path_view[i] == ']') ++i;
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else {
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
if (!is_array_like_reflected(current_type)) {
return false;
}
auto new_type = get_element_type_reflected(current_type);
if (new_type == ^^void) {
return false;
}
current_type = new_type;
}
} else {
++i;
}
}
return true;
}
};
// Compile-time path accessor with validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::ppc64::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<T, Path>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::ppc64::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<void, Path>;
return accessor::access(doc_or_val);
}
// ============================================================================
// JSON Pointer Compile-Time Support (RFC 6901)
// ============================================================================
// JSON Pointer parser: /field/0/nested (slash-separated)
template<constevalutil::fixed_string Pointer>
struct json_pointer_parser {
static constexpr std::string_view pointer_str = Pointer.view();
// Unescape token: ~0 -> ~, ~1 -> /
static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
out_len = 0;
for (std::size_t i = 0; i < src.size(); ++i) {
if (src[i] == '~' && i + 1 < src.size()) {
if (src[i + 1] == '0') {
dest[out_len++] = '~';
++i;
} else if (src[i + 1] == '1') {
dest[out_len++] = '/';
++i;
} else {
dest[out_len++] = src[i];
}
} else {
dest[out_len++] = src[i];
}
}
}
// Check if token is numeric
static consteval bool is_numeric(std::string_view token) {
if (token.empty()) return false;
if (token[0] == '0' && token.size() > 1) return false;
for (char c : token) {
if (c < '0' || c > '9') return false;
}
return true;
}
// Parse numeric token to index
static consteval std::size_t parse_index(std::string_view token) {
std::size_t result = 0;
for (char c : token) {
result = result * 10 + (c - '0');
}
return result;
}
// Count tokens in pointer
static consteval std::size_t count_tokens() {
if (pointer_str.empty() || pointer_str == "/") return 0;
std::size_t count = 0;
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
while (pos < pointer_str.size()) {
++count;
std::size_t next_slash = pointer_str.find('/', pos);
if (next_slash == std::string_view::npos) break;
pos = next_slash + 1;
}
return count;
}
// Get Nth token
static consteval std::string_view get_token(std::size_t token_index) {
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
std::size_t current_token = 0;
while (current_token < token_index) {
std::size_t next_slash = pointer_str.find('/', pos);
pos = next_slash + 1;
++current_token;
}
std::size_t token_end = pointer_str.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_str.size();
return pointer_str.substr(pos, token_end - pos);
}
};
// JSON Pointer accessor
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
using parser = json_pointer_parser<Pointer>;
static constexpr std::string_view pointer_view = Pointer.view();
static constexpr std::size_t token_count = parser::count_tokens();
// Validate pointer against struct definition
static consteval bool validate_pointer() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
// Extract token up to next /
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
return false;
}
current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
} else {
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) return false;
}
pos = token_end + 1;
}
return true;
}
// Recursive accessor
template<std::size_t TokenIndex>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if constexpr (TokenIndex >= token_count) {
return current;
} else {
constexpr std::string_view token = parser::get_token(TokenIndex);
if constexpr (parser::is_numeric(token)) {
constexpr std::size_t index = parser::parse_index(token);
auto arr = current.get_array().value_unsafe();
auto next_value = arr.at(index);
return access_impl<TokenIndex + 1>(next_value);
} else {
auto obj = current.get_object().value_unsafe();
auto next_value = obj.find_field_unordered(token);
return access_impl<TokenIndex + 1>(next_value);
}
}
}
// Access JSON value at pointer
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
if constexpr (std::is_class_v<T>) {
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
}
if (pointer_view.empty() || pointer_view == "/") {
if constexpr (requires { doc_or_val.get_value(); }) {
return doc_or_val.get_value();
} else {
return doc_or_val;
}
}
simdjson_result<value> current = doc_or_val.get_value();
return access_impl<0>(current);
}
// Extract value at pointer directly into target with type validation
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
constexpr auto final_type = get_final_type();
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
simdjson_result<value> current_value = doc_or_val.get_value();
auto json_value = access_impl<0>(current_value);
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get final type by walking pointer through struct
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
} else {
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
break;
}
}
}
pos = token_end + 1;
}
return current_type;
}
};
// Compile-time JSON Pointer accessor with validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::ppc64::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<T, Pointer>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::ppc64::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<void, Pointer>;
return accessor::access(doc_or_val);
}
} // namespace json_path
} // namespace ondemand
} // namespace ppc64
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* end file simdjson/generic/ondemand/compile_time_accessors.h for ppc64 */
/* end file simdjson/generic/ondemand/amalgamated.h for ppc64 */
/* including simdjson/ppc64/end.h: #include "simdjson/ppc64/end.h" */
/* begin file simdjson/ppc64/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/ppc64/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "ppc64" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/ppc64/end.h */
#endif // SIMDJSON_PPC64_ONDEMAND_H
/* end file simdjson/ppc64/ondemand.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(westmere)
/* including simdjson/westmere/ondemand.h: #include "simdjson/westmere/ondemand.h" */
/* begin file simdjson/westmere/ondemand.h */
#ifndef SIMDJSON_WESTMERE_ONDEMAND_H
#define SIMDJSON_WESTMERE_ONDEMAND_H
/* including simdjson/westmere/begin.h: #include "simdjson/westmere/begin.h" */
/* begin file simdjson/westmere/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "westmere" */
#define SIMDJSON_IMPLEMENTATION westmere
/* including simdjson/westmere/base.h: #include "simdjson/westmere/base.h" */
/* begin file simdjson/westmere/base.h */
#ifndef SIMDJSON_WESTMERE_BASE_H
#define SIMDJSON_WESTMERE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE
namespace simdjson {
/**
* Implementation for Westmere (Intel SSE4.2).
*/
namespace westmere {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BASE_H
/* end file simdjson/westmere/base.h */
/* including simdjson/westmere/intrinsics.h: #include "simdjson/westmere/intrinsics.h" */
/* begin file simdjson/westmere/intrinsics.h */
#ifndef SIMDJSON_WESTMERE_INTRINSICS_H
#define SIMDJSON_WESTMERE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
*/
#include <smmintrin.h> // for _mm_alignr_epi8
#include <wmmintrin.h> // for _mm_clmulepi64_si128
#endif
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere");
#endif // SIMDJSON_WESTMERE_INTRINSICS_H
/* end file simdjson/westmere/intrinsics.h */
#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE
SIMDJSON_TARGET_REGION("sse4.2,pclmul,popcnt")
#endif
/* including simdjson/westmere/bitmanipulation.h: #include "simdjson/westmere/bitmanipulation.h" */
/* begin file simdjson/westmere/bitmanipulation.h */
#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H
#define SIMDJSON_WESTMERE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H
/* end file simdjson/westmere/bitmanipulation.h */
/* including simdjson/westmere/bitmask.h: #include "simdjson/westmere/bitmask.h" */
/* begin file simdjson/westmere/bitmask.h */
#ifndef SIMDJSON_WESTMERE_BITMASK_H
#define SIMDJSON_WESTMERE_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) {
// There should be no such thing with a processing supporting avx2
// but not clmul.
__m128i all_ones = _mm_set1_epi8('\xFF');
__m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0);
return _mm_cvtsi128_si64(result);
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMASK_H
/* end file simdjson/westmere/bitmask.h */
/* including simdjson/westmere/numberparsing_defs.h: #include "simdjson/westmere/numberparsing_defs.h" */
/* begin file simdjson/westmere/numberparsing_defs.h */
#ifndef SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
#define SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
/* including simdjson/westmere/base.h: #include "simdjson/westmere/base.h" */
/* begin file simdjson/westmere/base.h */
#ifndef SIMDJSON_WESTMERE_BASE_H
#define SIMDJSON_WESTMERE_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE
namespace simdjson {
/**
* Implementation for Westmere (Intel SSE4.2).
*/
namespace westmere {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BASE_H
/* end file simdjson/westmere/base.h */
/* including simdjson/westmere/intrinsics.h: #include "simdjson/westmere/intrinsics.h" */
/* begin file simdjson/westmere/intrinsics.h */
#ifndef SIMDJSON_WESTMERE_INTRINSICS_H
#define SIMDJSON_WESTMERE_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_VISUAL_STUDIO
// under clang within visual studio, this will include <x86intrin.h>
#include <intrin.h> // visual studio or clang
#else
#include <x86intrin.h> // elsewhere
#endif // SIMDJSON_VISUAL_STUDIO
#if SIMDJSON_CLANG_VISUAL_STUDIO
/**
* You are not supposed, normally, to include these
* headers directly. Instead you should either include intrin.h
* or x86intrin.h. However, when compiling with clang
* under Windows (i.e., when _MSC_VER is set), these headers
* only get included *if* the corresponding features are detected
* from macros:
*/
#include <smmintrin.h> // for _mm_alignr_epi8
#include <wmmintrin.h> // for _mm_clmulepi64_si128
#endif
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere");
#endif // SIMDJSON_WESTMERE_INTRINSICS_H
/* end file simdjson/westmere/intrinsics.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace numberparsing {
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
// this actually computes *16* values so we are being wasteful.
const __m128i ascii0 = _mm_set1_epi8('0');
const __m128i mul_1_10 =
_mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1);
const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1);
const __m128i mul_1_10000 =
_mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1);
const __m128i input = _mm_sub_epi8(
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)), ascii0);
const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10);
const __m128i t2 = _mm_madd_epi16(t1, mul_1_100);
const __m128i t3 = _mm_packus_epi32(t2, t2);
const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000);
return _mm_cvtsi128_si32(
t4); // only captures the sum of the first 8 digits, drop the rest
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
#if SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
#if SIMDJSON_IS_ARM64
// ARM64 has native support for 64-bit multiplications, no need to emultate
answer.high = __umulh(value1, value2);
answer.low = value1 * value2;
#else
answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64
#endif // SIMDJSON_IS_ARM64
#else // SIMDJSON_REGULAR_VISUAL_STUDIO || SIMDJSON_IS_32BITS
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
#endif
return answer;
}
} // namespace numberparsing
} // namespace westmere
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_WESTMERE_NUMBERPARSING_DEFS_H
/* end file simdjson/westmere/numberparsing_defs.h */
/* including simdjson/westmere/simd.h: #include "simdjson/westmere/simd.h" */
/* begin file simdjson/westmere/simd.h */
#ifndef SIMDJSON_WESTMERE_SIMD_H
#define SIMDJSON_WESTMERE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
namespace simd {
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm_setzero_si128(); }
static simdjson_inline simd8<T> load(const T values[16]) {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m128i shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask
shufmask =
_mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m128i pruned = _mm_shuffle_epi8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8));
__m128i answer = _mm_shuffle_epi8(pruned, compactmask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm_min_epu8(*this, other); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm_testz_si128(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask() );
uint64_t r1 = this->chunks[1].to_bitmask() ;
uint64_t r2 = this->chunks[2].to_bitmask() ;
uint64_t r3 = this->chunks[3].to_bitmask() ;
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H
/* end file simdjson/westmere/simd.h */
/* including simdjson/westmere/stringparsing_defs.h: #include "simdjson/westmere/stringparsing_defs.h" */
/* begin file simdjson/westmere/stringparsing_defs.h */
#ifndef SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
#define SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
/* including simdjson/westmere/bitmanipulation.h: #include "simdjson/westmere/bitmanipulation.h" */
/* begin file simdjson/westmere/bitmanipulation.h */
#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H
#define SIMDJSON_WESTMERE_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long ret;
// Search the mask data from least significant bit (LSB)
// to the most significant bit (MSB) for a set bit (1).
_BitScanForward64(&ret, input_num);
return (int)ret;
#else // SIMDJSON_REGULAR_VISUAL_STUDIO
return __builtin_ctzll(input_num);
#endif // SIMDJSON_REGULAR_VISUAL_STUDIO
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
#if SIMDJSON_REGULAR_VISUAL_STUDIO
unsigned long leading_zero = 0;
// Search the mask data from most significant bit (MSB)
// to least significant bit (LSB) for a set bit (1).
if (_BitScanReverse64(&leading_zero, input_num))
return (int)(63 - leading_zero);
else
return 64;
#else
return __builtin_clzll(input_num);
#endif// SIMDJSON_REGULAR_VISUAL_STUDIO
}
#if SIMDJSON_REGULAR_VISUAL_STUDIO
simdjson_inline unsigned __int64 count_ones(uint64_t input_num) {
// note: we do not support legacy 32-bit Windows in this kernel
return __popcnt64(input_num);// Visual Studio wants two underscores
}
#else
simdjson_inline long long int count_ones(uint64_t input_num) {
return _popcnt64(input_num);
}
#endif
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H
/* end file simdjson/westmere/bitmanipulation.h */
/* including simdjson/westmere/simd.h: #include "simdjson/westmere/simd.h" */
/* begin file simdjson/westmere/simd.h */
#ifndef SIMDJSON_WESTMERE_SIMD_H
#define SIMDJSON_WESTMERE_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace {
namespace simd {
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); }
simdjson_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); }
simdjson_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
typedef uint16_t bitmask_t;
typedef uint32_t bitmask2_t;
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return _mm_set1_epi8(_value); }
static simdjson_inline simd8<T> zero() { return _mm_setzero_si128(); }
static simdjson_inline simd8<T> load(const T values[16]) {
return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm_add_epi8(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm_sub_epi8(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return _mm_shuffle_epi8(lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
// Design consideration: it seems like a function with the
// signature simd8<L> compress(uint32_t mask) would be
// sensible, but the AVX ISA makes this kind of approach difficult.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by work done by @animetosho
// we do it in two steps, first 8 bytes and then second 8 bytes
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register, using only
// two instructions on most compilers.
__m128i shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]);
// we increment by 0x08 the second half of the mask
shufmask =
_mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
// this is the version "nearly pruned"
__m128i pruned = _mm_shuffle_epi8(*this, shufmask);
// we still need to put the two halves together.
// we compute the popcount of the first half:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask, what it does is to write
// only the first pop1 bytes from the first 8 bytes, and then
// it fills in with the bytes from the second 8 bytes + some filling
// at the end.
__m128i compactmask =
_mm_loadu_si128(reinterpret_cast<const __m128i *>(pshufb_combine_table + pop1 * 8));
__m128i answer = _mm_shuffle_epi8(pruned, compactmask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm_max_epi8(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm_min_epi8(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(*this, other); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(other, *this); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t* values) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(_mm_setr_epi8(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
)) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm_adds_epu8(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm_subs_epu8(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm_max_epu8(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm_min_epu8(*this, other); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; }
simdjson_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm_testz_si128(*this, bits); }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
// Get one of the bits and make a bitmask out of it.
// e.g. value.get_bit<7>() gets the high bit
template<int N>
simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
this->chunks[0].compress(uint16_t(mask), output);
this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - count_ones(mask & 0xFFFF));
this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - count_ones(mask & 0xFFFFFFFF));
this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - count_ones(mask & 0xFFFFFFFFFFFF));
return 64 - count_ones(mask);
}
simdjson_inline uint64_t to_bitmask() const {
uint64_t r0 = uint32_t(this->chunks[0].to_bitmask() );
uint64_t r1 = this->chunks[1].to_bitmask() ;
uint64_t r2 = this->chunks[2].to_bitmask() ;
uint64_t r3 = this->chunks[3].to_bitmask() ;
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H
/* end file simdjson/westmere/simd.h */
namespace simdjson {
namespace westmere {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + 16);
v0.store(dst);
v1.store(dst + 16);
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_WESTMERE_STRINGPARSING_DEFS_H
/* end file simdjson/westmere/stringparsing_defs.h */
/* end file simdjson/westmere/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for westmere: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for westmere */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for westmere: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::westmere::number_type */
using number_type = simdjson::westmere::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for westmere */
/* including simdjson/generic/ondemand/deserialize.h for westmere: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for westmere */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<westmere::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<westmere::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<westmere::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<westmere::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = westmere::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = westmere::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = westmere::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = westmere::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = westmere::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = westmere::ondemand::array;
using object_type = westmere::ondemand::object;
using value_type = westmere::ondemand::value;
using document_type = westmere::ondemand::document;
using document_reference_type = westmere::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for westmere */
/* including simdjson/generic/ondemand/value_iterator.h for westmere: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::value_iterator> : public westmere::implementation_simdjson_result_base<westmere::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(westmere::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for westmere */
/* including simdjson/generic/ondemand/value.h for westmere: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::value> : public westmere::implementation_simdjson_result_base<westmere::ondemand::value> {
public:
simdjson_inline simdjson_result(westmere::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<westmere::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator westmere::ondemand::array() noexcept(false);
simdjson_inline operator westmere::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator westmere::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<westmere::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<westmere::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<westmere::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<westmere::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<westmere::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<westmere::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<westmere::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<westmere::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<westmere::ondemand::value>::get<westmere::ondemand::value>(
westmere::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<westmere::ondemand::value>
simdjson_result<westmere::ondemand::value>::get<westmere::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for westmere */
/* including simdjson/generic/ondemand/logger.h for westmere: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for westmere */
/* including simdjson/generic/ondemand/token_iterator.h for westmere: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::token_iterator> : public westmere::implementation_simdjson_result_base<westmere::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(westmere::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for westmere */
/* including simdjson/generic/ondemand/json_iterator.h for westmere: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::json_iterator> : public westmere::implementation_simdjson_result_base<westmere::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(westmere::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for westmere */
/* including simdjson/generic/ondemand/json_type.h for westmere: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::json_type> : public westmere::implementation_simdjson_result_base<westmere::ondemand::json_type> {
public:
simdjson_inline simdjson_result(westmere::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for westmere */
/* including simdjson/generic/ondemand/raw_json_string.h for westmere: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::raw_json_string> : public westmere::implementation_simdjson_result_base<westmere::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(westmere::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(westmere::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(westmere::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for westmere */
/* including simdjson/generic/ondemand/parser.h for westmere: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::parser> : public westmere::implementation_simdjson_result_base<westmere::ondemand::parser> {
public:
simdjson_inline simdjson_result(westmere::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for westmere */
// All other declarations
/* including simdjson/generic/ondemand/array.h for westmere: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::array> : public westmere::implementation_simdjson_result_base<westmere::ondemand::array> {
public:
simdjson_inline simdjson_result(westmere::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, westmere::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, westmere::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, westmere::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, westmere::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for westmere */
/* including simdjson/generic/ondemand/array_iterator.h for westmere: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::array_iterator> : public westmere::implementation_simdjson_result_base<westmere::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<westmere::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(westmere::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<westmere::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<westmere::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<westmere::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for westmere */
/* including simdjson/generic/ondemand/document.h for westmere: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::document> : public westmere::implementation_simdjson_result_base<westmere::ondemand::document> {
public:
simdjson_inline simdjson_result(westmere::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using westmere::implementation_simdjson_result_base<westmere::ondemand::document>::operator*;
using westmere::implementation_simdjson_result_base<westmere::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, westmere::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator westmere::ondemand::array() & noexcept(false);
simdjson_inline operator westmere::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator westmere::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator westmere::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<westmere::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<westmere::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::document_reference> : public westmere::implementation_simdjson_result_base<westmere::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(westmere::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator westmere::ondemand::array() & noexcept(false);
simdjson_inline operator westmere::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator westmere::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator westmere::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<westmere::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<westmere::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for westmere */
/* including simdjson/generic/ondemand/document_stream.h for westmere: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace westmere {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::document_stream> : public westmere::implementation_simdjson_result_base<westmere::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(westmere::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for westmere */
/* including simdjson/generic/ondemand/field.h for westmere: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::field> : public westmere::implementation_simdjson_result_base<westmere::ondemand::field> {
public:
simdjson_inline simdjson_result(westmere::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<westmere::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for westmere */
/* including simdjson/generic/ondemand/key_selector.h for westmere: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace westmere {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for westmere */
/* including simdjson/generic/ondemand/object.h for westmere: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::object> : public westmere::implementation_simdjson_result_base<westmere::ondemand::object> {
public:
simdjson_inline simdjson_result(westmere::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<westmere::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<westmere::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<westmere::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<westmere::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(westmere::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, westmere::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, westmere::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, westmere::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, westmere::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires westmere::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, westmere::ondemand::value>
simdjson_inline westmere::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, westmere::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires westmere::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(westmere::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline westmere::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(westmere::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(westmere::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline westmere::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(westmere::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for westmere */
/* including simdjson/generic/ondemand/object_iterator.h for westmere: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<westmere::ondemand::object_iterator> : public westmere::implementation_simdjson_result_base<westmere::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(westmere::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<westmere::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<westmere::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<westmere::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<westmere::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for westmere */
/* including simdjson/generic/ondemand/ranges.h for westmere: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::westmere::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::westmere::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for westmere */
/* including simdjson/generic/ondemand/serialization.h for westmere: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, westmere::ondemand::document> &&
!std::same_as<T, westmere::ondemand::value> &&
!std::same_as<T, westmere::ondemand::object> &&
!std::same_as<T, westmere::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace westmere { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::object> x);
#endif
}}} // namespace simdjson::westmere::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for westmere */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for westmere: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for westmere */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
westmere::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, westmere::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
westmere::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, westmere::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
westmere::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, westmere::ondemand::value &val, T &out) noexcept(false) {
westmere::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, westmere::ondemand::document &doc, T &out) noexcept(false) {
westmere::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, westmere::ondemand::document_reference &doc, T &out) noexcept(false) {
westmere::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, westmere::ondemand::number>
&& !std::is_same_v<T, westmere::ondemand::document>
&& !std::is_same_v<T, westmere::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^westmere::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = westmere::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, westmere::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
westmere::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
westmere::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
westmere::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
westmere::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, westmere::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
westmere::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, westmere::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, westmere::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, westmere::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for westmere */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for westmere: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::array>::simdjson_result(
westmere::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<westmere::ondemand::array>(
std::forward<westmere::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<westmere::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<westmere::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<westmere::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<westmere::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for westmere */
/* including simdjson/generic/ondemand/array_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::array_iterator>::simdjson_result(
westmere::ondemand::array_iterator &&value
) noexcept
: westmere::implementation_simdjson_result_base<westmere::ondemand::array_iterator>(std::forward<westmere::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: westmere::implementation_simdjson_result_base<westmere::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<westmere::ondemand::array_iterator>::operator==(const simdjson_result<westmere::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<westmere::ondemand::array_iterator>::operator!=(const simdjson_result<westmere::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> &simdjson_result<westmere::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<westmere::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for westmere */
/* including simdjson/generic/ondemand/value-inl.h for westmere: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace westmere {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::value>::simdjson_result(
westmere::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<westmere::ondemand::value>(
std::forward<westmere::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<westmere::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<westmere::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<westmere::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<westmere::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<westmere::ondemand::array> simdjson_result<westmere::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<westmere::ondemand::object> simdjson_result<westmere::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<westmere::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<westmere::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<westmere::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<westmere::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<westmere::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<westmere::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<westmere::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<westmere::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<westmere::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<westmere::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<westmere::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<westmere::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<westmere::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<westmere::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<westmere::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<westmere::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<westmere::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<westmere::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> simdjson_result<westmere::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<westmere::ondemand::value>::get<westmere::ondemand::value>(westmere::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<westmere::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<westmere::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::get<westmere::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<westmere::ondemand::json_type> simdjson_result<westmere::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<westmere::number_type> simdjson_result<westmere::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<westmere::ondemand::number> simdjson_result<westmere::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<westmere::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<westmere::ondemand::value>::operator westmere::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::value>::operator westmere::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::value>::operator westmere::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<westmere::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<westmere::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<westmere::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for westmere */
/* including simdjson/generic/ondemand/document-inl.h for westmere: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace westmere {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::document>::simdjson_result(
westmere::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<westmere::ondemand::document>(
std::forward<westmere::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<westmere::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<westmere::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<westmere::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<westmere::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<westmere::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<westmere::ondemand::array> simdjson_result<westmere::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<westmere::ondemand::object> simdjson_result<westmere::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<westmere::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<westmere::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<westmere::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<westmere::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<westmere::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<westmere::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<westmere::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<westmere::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<westmere::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<westmere::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<westmere::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<westmere::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<westmere::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<westmere::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<westmere::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<westmere::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<westmere::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> simdjson_result<westmere::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<westmere::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<westmere::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<westmere::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<westmere::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<westmere::ondemand::document> simdjson_result<westmere::ondemand::document>::get<westmere::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<westmere::ondemand::document> simdjson_result<westmere::ondemand::document>::get<westmere::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<westmere::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document>::get<westmere::ondemand::document>(westmere::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document>::get<westmere::ondemand::document>(westmere::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<westmere::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<westmere::ondemand::json_type> simdjson_result<westmere::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<westmere::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<westmere::number_type> simdjson_result<westmere::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<westmere::ondemand::number> simdjson_result<westmere::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, westmere::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<westmere::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator westmere::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator westmere::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator westmere::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<westmere::ondemand::document>::operator westmere::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<westmere::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<westmere::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<westmere::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<westmere::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace westmere {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::simdjson_result(westmere::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<westmere::ondemand::document_reference>(std::forward<westmere::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<westmere::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<westmere::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<westmere::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<westmere::ondemand::array_iterator> simdjson_result<westmere::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<westmere::ondemand::array> simdjson_result<westmere::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<westmere::ondemand::object> simdjson_result<westmere::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<westmere::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<westmere::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<westmere::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<westmere::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<westmere::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<westmere::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<westmere::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<westmere::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<westmere::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<westmere::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<westmere::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<westmere::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<westmere::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<westmere::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<westmere::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<westmere::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<westmere::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> simdjson_result<westmere::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<westmere::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<westmere::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<westmere::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, westmere::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<westmere::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<westmere::ondemand::json_type> simdjson_result<westmere::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document_reference>::get(westmere::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document_reference>::get(westmere::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<westmere::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<westmere::number_type> simdjson_result<westmere::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<westmere::ondemand::number> simdjson_result<westmere::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, westmere::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, westmere::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator westmere::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator westmere::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator westmere::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<westmere::ondemand::document_reference>::operator westmere::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<westmere::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<westmere::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for westmere */
/* including simdjson/generic/ondemand/document_stream-inl.h for westmere: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace westmere {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<westmere::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<westmere::ondemand::document_stream>::simdjson_result(
westmere::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<westmere::ondemand::document_stream>(
std::forward<westmere::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for westmere */
/* including simdjson/generic/ondemand/field-inl.h for westmere: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::field>::simdjson_result(
westmere::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<westmere::ondemand::field>(
std::forward<westmere::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<westmere::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<westmere::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string> simdjson_result<westmere::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<westmere::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<westmere::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<westmere::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for westmere */
/* including simdjson/generic/ondemand/json_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::json_iterator>::simdjson_result(westmere::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<westmere::ondemand::json_iterator>(std::forward<westmere::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<westmere::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<westmere::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for westmere */
/* including simdjson/generic/ondemand/json_type-inl.h for westmere: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::json_type>::simdjson_result(westmere::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<westmere::ondemand::json_type>(std::forward<westmere::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<westmere::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<westmere::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for westmere */
/* including simdjson/generic/ondemand/logger-inl.h for westmere: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace westmere {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for westmere */
/* including simdjson/generic/ondemand/object-inl.h for westmere: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::object>::simdjson_result(westmere::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<westmere::ondemand::object>(std::forward<westmere::ondemand::object>(value)) {}
simdjson_inline simdjson_result<westmere::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<westmere::ondemand::object>(error) {}
simdjson_inline simdjson_result<westmere::ondemand::object_iterator> simdjson_result<westmere::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<westmere::ondemand::object_iterator> simdjson_result<westmere::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<westmere::ondemand::object_iterator> simdjson_result<westmere::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<westmere::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<westmere::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<westmere::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<westmere::ondemand::value> simdjson_result<westmere::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, westmere::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<westmere::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires westmere::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, westmere::ondemand::value>
simdjson_inline westmere::ondemand::for_each_result
simdjson_result<westmere::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, westmere::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires westmere::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(westmere::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline westmere::ondemand::for_each_result
simdjson_result<westmere::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(westmere::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(westmere::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline westmere::ondemand::for_each_result
simdjson_result<westmere::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(westmere::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<westmere::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<westmere::ondemand::object_position> simdjson_result<westmere::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<westmere::ondemand::object>::revert_position(westmere::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<westmere::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<westmere::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<westmere::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for westmere */
/* including simdjson/generic/ondemand/object_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::object_iterator>::simdjson_result(
westmere::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<westmere::ondemand::object_iterator>(std::forward<westmere::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<westmere::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<westmere::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<westmere::ondemand::field> simdjson_result<westmere::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<westmere::ondemand::object_iterator>::operator==(const simdjson_result<westmere::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<westmere::ondemand::object_iterator>::operator!=(const simdjson_result<westmere::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<westmere::ondemand::object_iterator> &simdjson_result<westmere::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for westmere */
/* including simdjson/generic/ondemand/ranges-inl.h for westmere: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace westmere {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::westmere::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::westmere::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::westmere::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::westmere::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::westmere::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::westmere::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for westmere */
/* including simdjson/generic/ondemand/parser-inl.h for westmere: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::parser>::simdjson_result(westmere::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<westmere::ondemand::parser>(std::forward<westmere::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<westmere::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<westmere::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for westmere */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for westmere: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string>::simdjson_result(westmere::ondemand::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<westmere::ondemand::raw_json_string>(std::forward<westmere::ondemand::raw_json_string>(value)) {}
simdjson_inline simdjson_result<westmere::ondemand::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<westmere::ondemand::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<westmere::ondemand::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline char simdjson_result<westmere::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
if (error()) { return error(); }
return first[i];
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<westmere::ondemand::raw_json_string>::unescape(westmere::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<westmere::ondemand::raw_json_string>::unescape_wobbly(westmere::ondemand::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* end file simdjson/generic/ondemand/raw_json_string-inl.h for westmere */
/* including simdjson/generic/ondemand/token_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/token_iterator-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::token_iterator>::simdjson_result(westmere::ondemand::token_iterator &&value) noexcept
: implementation_simdjson_result_base<westmere::ondemand::token_iterator>(std::forward<westmere::ondemand::token_iterator>(value)) {}
simdjson_inline simdjson_result<westmere::ondemand::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<westmere::ondemand::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/token_iterator-inl.h for westmere */
/* including simdjson/generic/ondemand/value_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/value_iterator-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace westmere {
namespace ondemand {
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
bool escaping{false};
for (uint32_t i = 1; i < max_len; i++) {
switch (json[i]) {
case '"':
if (!escaping) { return true; }
escaping = false;
break;
case '\\':
escaping = !escaping;
break;
default:
escaping = false;
break;
}
}
return false;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
SIMDJSON_TRY(end_container());
return false;
}
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
// call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != '}') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
// If the last character is } *and* the first gibberish character is also '}'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed object.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
auto error = check_root_object();
if(error) { return error; }
return started_object();
}
simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
#if SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
return false;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return false;
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
// If someone queried a key but they not did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
//
// ```
// // At the beginning of a field
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // At the end of the object
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// ```
//
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
SIMDJSON_TRY(reset_object().get(has_value));
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
return raw_json_string(key);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
assert_at_next();
const uint8_t *k = _json_iter->return_current_and_advance();
if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
// After return_current_and_advance(), the current token is the ':' that follows
// the key. The closing quote sits just before it (only JSON whitespace may
// intervene), so step back from the ':' to the closing quote to get the length.
// In minified JSON this is a single back-step.
const char *q = reinterpret_cast<const char *>(_json_iter->peek());
do { --q; } while (*q != '"');
key = raw_json_string(k);
len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
assert_at_next();
if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
answer += std::string(" ]");
return answer;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
SIMDJSON_TRY( end_container() );
return false;
}
_json_iter->descend_to(depth()+1);
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
// also call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != ']') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
// If the last character is ] *and* the first gibberish character is also ']'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed array.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
auto error = check_root_array();
if (error) { return error; }
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
}
simdjson_inline bool value_iterator::is_root_negative() noexcept {
return numberparsing::is_negative(peek_root_scalar("numbersign"));
}
simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
return numberparsing::is_integer(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
return numberparsing::get_number_type(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
number num;
error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
if(error) { return error; }
return num;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<westmere::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_inline value_iterator value_iterator::child() const noexcept {
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool value_iterator::at_end() const noexcept {
return _json_iter->at_end();
}
simdjson_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.position() == start_position();
}
simdjson_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
return _json_iter->error;
}
simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
return _json_iter->string_buf_loc();
}
simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
return *_json_iter;
}
simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
return *_json_iter;
}
simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(start_position());
}
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
logger::log_start_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
const uint8_t *json;
if (!is_at_start()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
return SUCCESS;
}
simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
return _json_iter->peek();
}
simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
logger::log_error(*_json_iter, start_position(), depth(), message);
return INCORRECT_TYPE;
}
simdjson_inline bool value_iterator::is_at_start() const noexcept {
return position() == start_position();
}
simdjson_inline bool value_iterator::is_at_key() const noexcept {
// Keys are at the same depth as the object.
// Note here that we could be safer and check that we are within an object,
// but we do not.
//
// As long as we are at the object's depth, in a valid document,
// we will only ever be at { , : or the actual string key: ".
return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
}
simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = position() - start_position();
return delta == 1 || delta == 2;
}
inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_inline void value_iterator::move_at_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position);
}
simdjson_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
// Unlike reenter_child(), this does not require the live depth to be
// exactly one level shallower than depth, nor does it validate against
// the parser's per-depth container-start bookkeeping: neither holds in
// general for a caller-supplied snapshot (see object_position). What
// must still always hold, regardless of what was captured or how far
// the live iterator has since moved, is that position and depth are
// themselves sane values -- this is the same bound reenter_child()
// itself applies unconditionally.
SIMDJSON_ASSUME(position != nullptr);
SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
_json_iter->_depth = depth;
_json_iter->token.set_position(position);
}
simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_array();
}
simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_object();
}
inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return json_type::unknown;
}
}
simdjson_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<westmere::ondemand::value_iterator>::simdjson_result(westmere::ondemand::value_iterator &&value) noexcept
: implementation_simdjson_result_base<westmere::ondemand::value_iterator>(std::forward<westmere::ondemand::value_iterator>(value)) {}
simdjson_inline simdjson_result<westmere::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<westmere::ondemand::value_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/value_iterator-inl.h for westmere */
/* including simdjson/generic/ondemand/serialization-inl.h for westmere: #include "simdjson/generic/ondemand/serialization-inl.h" */
/* begin file simdjson/generic/ondemand/serialization-inl.h for westmere */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-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/serialization.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace westmere::ondemand;
westmere::ondemand::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
westmere::ondemand::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
westmere::ondemand::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(westmere::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<westmere::ondemand::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace westmere { namespace ondemand {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::westmere::ondemand::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::westmere::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::westmere::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* end file simdjson/generic/ondemand/serialization-inl.h for westmere */
// JSON path accessor (compile-time) - must be after inline definitions
/* including simdjson/generic/ondemand/compile_time_accessors.h for westmere: #include "simdjson/generic/ondemand/compile_time_accessors.h" */
/* begin file simdjson/generic/ondemand/compile_time_accessors.h for westmere */
/**
* Compile-time JSON Path and JSON Pointer accessors using C++26 reflection (P2996)
*
* This file validates JSON paths/pointers against struct definitions at compile time
* and generates optimized accessor code with zero runtime overhead.
*
* ## How It Works
*
* **Compile Time**: Path is parsed, validated against struct, types are checked
* **Runtime**: Direct navigation with no parsing or validation overhead
*
* Example:
* ```cpp
* struct User { std::string name; std::vector<std::string> emails; };
*
* std::string email;
* path_accessor<User, ".emails[0]">::extract_field(doc, email);
*
* // Compile time validates:
* // 1. User has "emails" field
* // 2. "emails" is array-like
* // 3. Element type is std::string
* // 4. static_assert(^^std::string == ^^std::string)
*
* // Runtime just navigates:
* // doc.get_object().find_field("emails").get_array().at(0).get(email)
* ```
*
* ## Key Reflection APIs
*
* - `^^Type`: Reflect operator, converts type to std::meta::info
* - `std::meta::nonstatic_data_members_of(type)`: Get all fields of a struct
* - `std::meta::identifier_of(member)`: Get field name as string_view
* - `std::meta::type_of(member)`: Get reflected type of a field
* - `std::meta::is_array_type(type)`: Check if C-style array
* - `std::meta::remove_extent(array)`: Extract element type from array
* - `std::meta::members_of(type)`: Get all members including typedefs
* - `std::meta::is_type(member)`: Check if member is a type (vs field)
*
* All operations execute at compile time in consteval contexts.
*/
#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H */
/* amalgamation skipped (editor-only): */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
// is unlikely that we will have reflection support without concepts support.
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#include <string_view>
#include <cstddef>
#include <array>
namespace simdjson {
namespace westmere {
namespace ondemand {
/***
* JSONPath implementation for compile-time access
* RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
*/
namespace json_path {
// Note: value type must be fully defined before this header is included
// This is ensured by including this in amalgamated.h after value-inl.h
using ::simdjson::westmere::ondemand::value;
// Path step types
enum class step_type {
field, // .field_name or ["field_name"]
array_index // [index]
};
// Represents a single step in a JSON path
template<std::size_t N>
struct path_step {
step_type type;
char key[N]; // Field name (empty for array indices)
std::size_t index; // Array index (0 for field access)
constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
: type(t), index(idx) {
for (std::size_t i = 0; i < N; ++i) {
key[i] = k[i];
}
}
constexpr std::string_view key_view() const {
return {key, N - 1};
}
};
// Helper to create field step
template<std::size_t N>
consteval auto make_field_step(const char (&name)[N]) {
return path_step<N>(step_type::field, name, 0);
}
// Helper to create array index step
consteval auto make_index_step(std::size_t idx) {
return path_step<1>(step_type::array_index, "", idx);
}
// Parse state for compile-time JSON path parsing
struct parse_result {
bool success;
std::size_t pos;
std::string_view error_msg;
};
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
struct json_path_parser {
static constexpr std::string_view path_str = Path.view();
// Skip leading $ if present
static consteval std::size_t skip_root() {
if (!path_str.empty() && path_str[0] == '$') {
return 1;
}
return 0;
}
// Count the number of steps in the path at compile time
static consteval std::size_t count_steps() {
std::size_t count = 0;
std::size_t i = skip_root();
while (i < path_str.size()) {
if (path_str[i] == '.') {
// Field access: .field
++i;
if (i >= path_str.size()) break;
// Skip field name
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
++i;
}
++count;
} else if (path_str[i] == '[') {
// Array or bracket notation
++i;
if (i >= path_str.size()) break;
if (path_str[i] == '"' || path_str[i] == '\'') {
// Field access: ["field"] or ['field']
char quote = path_str[i];
++i;
while (i < path_str.size() && path_str[i] != quote) {
++i;
}
if (i < path_str.size()) ++i; // skip closing quote
if (i < path_str.size() && path_str[i] == ']') ++i;
} else {
// Array index: [0], [123]
while (i < path_str.size() && path_str[i] != ']') {
++i;
}
if (i < path_str.size()) ++i; // skip ]
}
++count;
} else {
++i;
}
}
return count;
}
// Parse a field name at compile time
static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
std::size_t len = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
out[len++] = path_str[i++];
}
out[len] = '\0';
return i;
}
// Parse an array index at compile time
static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
std::size_t index = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
index = index * 10 + (path_str[i] - '0');
++i;
}
return {i, index};
}
};
// Compile-time path accessor generator
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
using value = ::simdjson::westmere::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
// If T is a struct, validates the path at compile time
// If T is void, skips validation
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
// Validate path at compile time if T is a struct
if constexpr (std::is_class_v<T>) {
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
}
// Parse the path at compile time to build access steps
return access_impl<parser.skip_root()>(doc_or_val.get_value());
}
// Extract value at path directly into target with compile-time type validation
// Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
// Validate path exists in struct definition
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
// Get the type at the end of the path
constexpr auto final_type = get_final_type();
// Verify target type matches the field type
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
// All validation done at compile time - just navigate and extract
auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get the final type by walking the path through the struct type
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
// .field syntax
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) break;
if (path_view[i] == '"' || path_view[i] == '\'') {
// ["field"] syntax
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip quote
if (i < path_view.size() && path_view[i] == ']') ++i;
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else {
// [index] syntax - extract element type
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
current_type = get_element_type_reflected(current_type);
}
} else {
++i;
}
}
return current_type;
}
private:
// Walk path and extract directly into final field using compile-time reflection
template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
if (current.error()) return current.error();
// Base case: end of path - extract into target
if constexpr (PathPos >= path_view.size()) {
return current.get(target_ref);
}
// Field access: .field_name
else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
}
// Bracket notation: [index] or ["field"]
else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
constexpr auto elem_type = get_element_type_reflected(CurrentType);
static_assert(elem_type != ^^void, "Could not determine array element type");
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto elem_value = arr.at(index);
if constexpr (next_pos >= path_view.size()) {
return elem_value.get(target_ref);
} else {
return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
}
}
}
// Skip unexpected characters and continue
else {
return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
}
}
// Find member by name in reflected type
static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == name) {
return mem;
}
}
}
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if (current.error()) return current;
if constexpr (PathPos >= path_view.size()) {
return current;
} else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto next_value = arr.at(index);
return access_impl<next_pos>(next_value);
}
} else {
return access_impl<PathPos + 1>(current);
}
}
// Parse next field name
static consteval auto parse_next_field(std::size_t start) {
std::size_t i = start + 1;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
return std::make_tuple(field_name, i);
}
// Parse bracket notation: returns (is_field, field_name, next_pos, index)
static consteval auto parse_bracket(std::size_t start) {
std::size_t i = start + 1; // skip '['
if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
// Field access
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip closing quote
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(true, field_name, i, std::size_t(0));
} else {
// Array index
std::size_t index = 0;
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
index = index * 10 + (path_view[i] - '0');
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(false, std::string_view{}, i, index);
}
}
public:
// Check if reflected type is array-like (C-style array or indexable container)
// Uses reflection to test: 1) std::meta::is_array_type() for C arrays
// 2) std::meta::substitute() to test concepts::indexable_container concept
static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return true;
}
if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
}
return false;
}
// Extract element type from reflected array or container
// For C arrays: uses std::meta::remove_extent()
// For containers: finds value_type member using std::meta::members_of()
static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return std::meta::remove_extent(type_reflection);
}
auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::is_type(mem)) {
auto name = std::meta::identifier_of(mem);
if (name == "value_type") {
return mem;
}
}
}
return ^^void;
}
private:
// Check if type has member with given name
template<typename Type>
static consteval bool has_member(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return true;
}
}
return false;
}
// Get type of member by name
template<typename Type>
static consteval auto get_member_type(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return std::meta::type_of(mem);
}
}
return ^^void;
}
// Check if non-reflected type is array-like
template<typename Type>
static consteval bool is_container_type() {
using BaseType = std::remove_cvref_t<Type>;
if constexpr (requires { typename BaseType::value_type; }) {
return true;
}
if constexpr (std::is_array_v<BaseType>) {
return true;
}
return false;
}
// Extract element type from non-reflected container
template<typename Type>
using extract_element_type = std::conditional_t<
requires { typename std::remove_cvref_t<Type>::value_type; },
typename std::remove_cvref_t<Type>::value_type,
std::conditional_t<
std::is_array_v<std::remove_cvref_t<Type>>,
std::remove_extent_t<std::remove_cvref_t<Type>>,
void
>
>;
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) return false;
if (path_view[i] == '"' || path_view[i] == '\'') {
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i;
if (i < path_view.size() && path_view[i] == ']') ++i;
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else {
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
if (!is_array_like_reflected(current_type)) {
return false;
}
auto new_type = get_element_type_reflected(current_type);
if (new_type == ^^void) {
return false;
}
current_type = new_type;
}
} else {
++i;
}
}
return true;
}
};
// Compile-time path accessor with validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::westmere::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<T, Path>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::westmere::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<void, Path>;
return accessor::access(doc_or_val);
}
// ============================================================================
// JSON Pointer Compile-Time Support (RFC 6901)
// ============================================================================
// JSON Pointer parser: /field/0/nested (slash-separated)
template<constevalutil::fixed_string Pointer>
struct json_pointer_parser {
static constexpr std::string_view pointer_str = Pointer.view();
// Unescape token: ~0 -> ~, ~1 -> /
static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
out_len = 0;
for (std::size_t i = 0; i < src.size(); ++i) {
if (src[i] == '~' && i + 1 < src.size()) {
if (src[i + 1] == '0') {
dest[out_len++] = '~';
++i;
} else if (src[i + 1] == '1') {
dest[out_len++] = '/';
++i;
} else {
dest[out_len++] = src[i];
}
} else {
dest[out_len++] = src[i];
}
}
}
// Check if token is numeric
static consteval bool is_numeric(std::string_view token) {
if (token.empty()) return false;
if (token[0] == '0' && token.size() > 1) return false;
for (char c : token) {
if (c < '0' || c > '9') return false;
}
return true;
}
// Parse numeric token to index
static consteval std::size_t parse_index(std::string_view token) {
std::size_t result = 0;
for (char c : token) {
result = result * 10 + (c - '0');
}
return result;
}
// Count tokens in pointer
static consteval std::size_t count_tokens() {
if (pointer_str.empty() || pointer_str == "/") return 0;
std::size_t count = 0;
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
while (pos < pointer_str.size()) {
++count;
std::size_t next_slash = pointer_str.find('/', pos);
if (next_slash == std::string_view::npos) break;
pos = next_slash + 1;
}
return count;
}
// Get Nth token
static consteval std::string_view get_token(std::size_t token_index) {
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
std::size_t current_token = 0;
while (current_token < token_index) {
std::size_t next_slash = pointer_str.find('/', pos);
pos = next_slash + 1;
++current_token;
}
std::size_t token_end = pointer_str.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_str.size();
return pointer_str.substr(pos, token_end - pos);
}
};
// JSON Pointer accessor
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
using parser = json_pointer_parser<Pointer>;
static constexpr std::string_view pointer_view = Pointer.view();
static constexpr std::size_t token_count = parser::count_tokens();
// Validate pointer against struct definition
static consteval bool validate_pointer() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
// Extract token up to next /
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
return false;
}
current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
} else {
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) return false;
}
pos = token_end + 1;
}
return true;
}
// Recursive accessor
template<std::size_t TokenIndex>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if constexpr (TokenIndex >= token_count) {
return current;
} else {
constexpr std::string_view token = parser::get_token(TokenIndex);
if constexpr (parser::is_numeric(token)) {
constexpr std::size_t index = parser::parse_index(token);
auto arr = current.get_array().value_unsafe();
auto next_value = arr.at(index);
return access_impl<TokenIndex + 1>(next_value);
} else {
auto obj = current.get_object().value_unsafe();
auto next_value = obj.find_field_unordered(token);
return access_impl<TokenIndex + 1>(next_value);
}
}
}
// Access JSON value at pointer
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
if constexpr (std::is_class_v<T>) {
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
}
if (pointer_view.empty() || pointer_view == "/") {
if constexpr (requires { doc_or_val.get_value(); }) {
return doc_or_val.get_value();
} else {
return doc_or_val;
}
}
simdjson_result<value> current = doc_or_val.get_value();
return access_impl<0>(current);
}
// Extract value at pointer directly into target with type validation
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
constexpr auto final_type = get_final_type();
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
simdjson_result<value> current_value = doc_or_val.get_value();
auto json_value = access_impl<0>(current_value);
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get final type by walking pointer through struct
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
} else {
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
break;
}
}
}
pos = token_end + 1;
}
return current_type;
}
};
// Compile-time JSON Pointer accessor with validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::westmere::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<T, Pointer>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::westmere::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<void, Pointer>;
return accessor::access(doc_or_val);
}
} // namespace json_path
} // namespace ondemand
} // namespace westmere
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* end file simdjson/generic/ondemand/compile_time_accessors.h for westmere */
/* end file simdjson/generic/ondemand/amalgamated.h for westmere */
/* including simdjson/westmere/end.h: #include "simdjson/westmere/end.h" */
/* begin file simdjson/westmere/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/westmere/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE
SIMDJSON_UNTARGET_REGION
#endif
/* undefining SIMDJSON_IMPLEMENTATION from "westmere" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/westmere/end.h */
#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H
/* end file simdjson/westmere/ondemand.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lsx)
/* including simdjson/lsx/ondemand.h: #include "simdjson/lsx/ondemand.h" */
/* begin file simdjson/lsx/ondemand.h */
#ifndef SIMDJSON_LSX_ONDEMAND_H
#define SIMDJSON_LSX_ONDEMAND_H
/* including simdjson/lsx/begin.h: #include "simdjson/lsx/begin.h" */
/* begin file simdjson/lsx/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "lsx" */
#define SIMDJSON_IMPLEMENTATION lsx
/* including simdjson/lsx/base.h: #include "simdjson/lsx/base.h" */
/* begin file simdjson/lsx/base.h */
#ifndef SIMDJSON_LSX_BASE_H
#define SIMDJSON_LSX_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for LSX.
*/
namespace lsx {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_BASE_H
/* end file simdjson/lsx/base.h */
/* including simdjson/lsx/intrinsics.h: #include "simdjson/lsx/intrinsics.h" */
/* begin file simdjson/lsx/intrinsics.h */
#ifndef SIMDJSON_LSX_INTRINSICS_H
#define SIMDJSON_LSX_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <lsxintrin.h>
static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch SX");
#endif // SIMDJSON_LSX_INTRINSICS_H
/* end file simdjson/lsx/intrinsics.h */
/* including simdjson/lsx/bitmanipulation.h: #include "simdjson/lsx/bitmanipulation.h" */
/* begin file simdjson/lsx/bitmanipulation.h */
#ifndef SIMDJSON_LSX_BITMANIPULATION_H
#define SIMDJSON_LSX_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
return __lsx_vpickve2gr_w(__lsx_vpcnt_d(__m128i(v2u64{input_num, 0})), 0);
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_BITMANIPULATION_H
/* end file simdjson/lsx/bitmanipulation.h */
/* including simdjson/lsx/bitmask.h: #include "simdjson/lsx/bitmask.h" */
/* begin file simdjson/lsx/bitmask.h */
#ifndef SIMDJSON_LSX_BITMASK_H
#define SIMDJSON_LSX_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif
/* end file simdjson/lsx/bitmask.h */
/* including simdjson/lsx/numberparsing_defs.h: #include "simdjson/lsx/numberparsing_defs.h" */
/* begin file simdjson/lsx/numberparsing_defs.h */
#ifndef SIMDJSON_LSX_NUMBERPARSING_DEFS_H
#define SIMDJSON_LSX_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace lsx {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace lsx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H
/* end file simdjson/lsx/numberparsing_defs.h */
/* including simdjson/lsx/simd.h: #include "simdjson/lsx/simd.h" */
/* begin file simdjson/lsx/simd.h */
#ifndef SIMDJSON_LSX_SIMD_H
#define SIMDJSON_LSX_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m128i value;
// Zero constructor
simdjson_inline base() : value{__m128i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m128i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m128i&() const { return this->value; }
simdjson_inline operator __m128i&() { return this->value; }
simdjson_inline operator const v16i8&() const { return (v16i8&)this->value; }
simdjson_inline operator v16i8&() { return (v16i8&)this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return __lsx_vor_v(*this, other); }
simdjson_inline Child operator&(const Child other) const { return __lsx_vand_v(*this, other); }
simdjson_inline Child operator^(const Child other) const { return __lsx_vxor_v(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return __lsx_vandn_v(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return __lsx_vseq_b(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
return __lsx_vor_v(__lsx_vbsll_v(*this, N), __lsx_vbsrl_v(prev_chunk, 16 - N));
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) {
return __lsx_vreplgr2vr_b(uint8_t(-(!!_value)));
}
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m128i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const { return __lsx_vpickve2gr_w(__lsx_vmskltz_b(*this), 0); }
simdjson_inline bool any() const { return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(*this), 0); }
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) { return __lsx_vreplgr2vr_b(_value); }
static simdjson_inline simd8<T> zero() { return __lsx_vldi(0); }
static simdjson_inline simd8<T> load(const T values[16]) {
return __lsx_vld(reinterpret_cast<const __m128i *>(values), 0);
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[16]) const {
return __lsx_vst(*this, reinterpret_cast<__m128i *>(dst), 0);
}
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return __lsx_vadd_b(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return __lsx_vsub_b(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __lsx_vshuf_b(lookup_table, lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
template<typename L>
simdjson_inline void compress(uint16_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by haswell
// lsx do it in 2 steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second least significant 8 bits
// next line just loads the 64-bit values thintable_epi8[mask1] and
// thintable_epi8[mask2] into a 128-bit register.
__m128i shufmask = {int64_t(thintable_epi8[mask1]), int64_t(thintable_epi8[mask2]) + 0x0808080808080808};
// this is the version "nearly pruned"
__m128i pruned = __lsx_vshuf_b(*this, *this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
// then load the corresponding mask
__m128i compactmask = __lsx_vldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop1 * 8);
__m128i answer = __lsx_vshuf_b(pruned, pruned, compactmask);
__lsx_vst(answer, reinterpret_cast<uint8_t*>(output), 0);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[16]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8({
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return __lsx_vmax_b(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return __lsx_vmin_b(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return __lsx_vslt_b(other, *this); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return __lsx_vslt_b(*this, other); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[16]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(__m128i(v16u8{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
})) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __lsx_vsadd_bu(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __lsx_vssub_bu(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return __lsx_vmax_bu(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return __lsx_vmin_bu(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const { return 0 == __lsx_vpickve2gr_w(__lsx_vmskltz_b(*this), 0); }
simdjson_inline bool bits_not_set_anywhere() const { return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(*this), 0); }
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
return 0 == __lsx_vpickve2gr_hu(__lsx_vmsknz_b(__lsx_vand_v(*this, bits)), 0);
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(__lsx_vsrli_b(*this, N)); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(__lsx_vslli_b(*this, N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 4, "LSX kernel should use four registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+16), simd8<T>::load(ptr+32), simd8<T>::load(ptr+48)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint16_t mask1 = uint16_t(mask);
uint16_t mask2 = uint16_t(mask >> 16);
uint16_t mask3 = uint16_t(mask >> 32);
uint16_t mask4 = uint16_t(mask >> 48);
__m128i zcnt = __lsx_vpcnt_h(__m128i(v2u64{~mask, 0}));
uint64_t zcnt1 = __lsx_vpickve2gr_hu(zcnt, 0);
uint64_t zcnt2 = __lsx_vpickve2gr_hu(zcnt, 1);
uint64_t zcnt3 = __lsx_vpickve2gr_hu(zcnt, 2);
uint64_t zcnt4 = __lsx_vpickve2gr_hu(zcnt, 3);
uint8_t *voutput = reinterpret_cast<uint8_t*>(output);
// There should be a critical value which processes in scaler is faster.
if (zcnt1)
this->chunks[0].compress(mask1, reinterpret_cast<T*>(voutput));
voutput += zcnt1;
if (zcnt2)
this->chunks[1].compress(mask2, reinterpret_cast<T*>(voutput));
voutput += zcnt2;
if (zcnt3)
this->chunks[2].compress(mask3, reinterpret_cast<T*>(voutput));
voutput += zcnt3;
if (zcnt4)
this->chunks[3].compress(mask4, reinterpret_cast<T*>(voutput));
voutput += zcnt4;
return reinterpret_cast<uint64_t>(voutput) - reinterpret_cast<uint64_t>(output);
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
this->chunks[2].store(ptr+sizeof(simd8<T>)*2);
this->chunks[3].store(ptr+sizeof(simd8<T>)*3);
}
simdjson_inline uint64_t to_bitmask() const {
__m128i mask1 = __lsx_vmskltz_b(this->chunks[0]);
__m128i mask2 = __lsx_vmskltz_b(this->chunks[1]);
__m128i mask3 = __lsx_vmskltz_b(this->chunks[2]);
__m128i mask4 = __lsx_vmskltz_b(this->chunks[3]);
mask1 = __lsx_vilvl_h(mask2, mask1);
mask2 = __lsx_vilvl_h(mask4, mask3);
return __lsx_vpickve2gr_du(__lsx_vilvl_w(mask2, mask1), 0);
}
simdjson_inline simd8<T> reduce_or() const {
return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask,
this->chunks[2] == mask,
this->chunks[3] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1],
this->chunks[2] == other.chunks[2],
this->chunks[3] == other.chunks[3]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask,
this->chunks[2] <= mask,
this->chunks[3] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_SIMD_H
/* end file simdjson/lsx/simd.h */
/* including simdjson/lsx/stringparsing_defs.h: #include "simdjson/lsx/stringparsing_defs.h" */
/* begin file simdjson/lsx/stringparsing_defs.h */
#ifndef SIMDJSON_LSX_STRINGPARSING_DEFS_H
#define SIMDJSON_LSX_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v0(src);
simd8<uint8_t> v1(src + sizeof(v0));
v0.store(dst);
v1.store(dst + sizeof(v0));
// Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on LSX; therefore, we
// smash them together into a 64-byte mask and get the bitmask from there.
uint64_t bs_and_quote = simd8x64<bool>(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask();
return {
uint32_t(bs_and_quote), // bs_bits
uint32_t(bs_and_quote >> 32) // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_LSX_STRINGPARSING_DEFS_H
/* end file simdjson/lsx/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/lsx/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for lsx: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for lsx */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for lsx: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::lsx::number_type */
using number_type = simdjson::lsx::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for lsx */
/* including simdjson/generic/ondemand/deserialize.h for lsx: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for lsx */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<lsx::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<lsx::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<lsx::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<lsx::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = lsx::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = lsx::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = lsx::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = lsx::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = lsx::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = lsx::ondemand::array;
using object_type = lsx::ondemand::object;
using value_type = lsx::ondemand::value;
using document_type = lsx::ondemand::document;
using document_reference_type = lsx::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for lsx */
/* including simdjson/generic/ondemand/value_iterator.h for lsx: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::value_iterator> : public lsx::implementation_simdjson_result_base<lsx::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(lsx::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for lsx */
/* including simdjson/generic/ondemand/value.h for lsx: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::value> : public lsx::implementation_simdjson_result_base<lsx::ondemand::value> {
public:
simdjson_inline simdjson_result(lsx::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<lsx::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator lsx::ondemand::array() noexcept(false);
simdjson_inline operator lsx::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator lsx::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<lsx::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<lsx::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<lsx::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<lsx::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<lsx::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<lsx::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<lsx::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<lsx::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<lsx::ondemand::value>::get<lsx::ondemand::value>(
lsx::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<lsx::ondemand::value>
simdjson_result<lsx::ondemand::value>::get<lsx::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for lsx */
/* including simdjson/generic/ondemand/logger.h for lsx: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for lsx */
/* including simdjson/generic/ondemand/token_iterator.h for lsx: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::token_iterator> : public lsx::implementation_simdjson_result_base<lsx::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(lsx::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for lsx */
/* including simdjson/generic/ondemand/json_iterator.h for lsx: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::json_iterator> : public lsx::implementation_simdjson_result_base<lsx::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(lsx::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for lsx */
/* including simdjson/generic/ondemand/json_type.h for lsx: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::json_type> : public lsx::implementation_simdjson_result_base<lsx::ondemand::json_type> {
public:
simdjson_inline simdjson_result(lsx::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for lsx */
/* including simdjson/generic/ondemand/raw_json_string.h for lsx: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::raw_json_string> : public lsx::implementation_simdjson_result_base<lsx::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(lsx::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(lsx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(lsx::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for lsx */
/* including simdjson/generic/ondemand/parser.h for lsx: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::parser> : public lsx::implementation_simdjson_result_base<lsx::ondemand::parser> {
public:
simdjson_inline simdjson_result(lsx::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for lsx */
// All other declarations
/* including simdjson/generic/ondemand/array.h for lsx: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::array> : public lsx::implementation_simdjson_result_base<lsx::ondemand::array> {
public:
simdjson_inline simdjson_result(lsx::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, lsx::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, lsx::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, lsx::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, lsx::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for lsx */
/* including simdjson/generic/ondemand/array_iterator.h for lsx: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::array_iterator> : public lsx::implementation_simdjson_result_base<lsx::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<lsx::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(lsx::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<lsx::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<lsx::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<lsx::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for lsx */
/* including simdjson/generic/ondemand/document.h for lsx: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::document> : public lsx::implementation_simdjson_result_base<lsx::ondemand::document> {
public:
simdjson_inline simdjson_result(lsx::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using lsx::implementation_simdjson_result_base<lsx::ondemand::document>::operator*;
using lsx::implementation_simdjson_result_base<lsx::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, lsx::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator lsx::ondemand::array() & noexcept(false);
simdjson_inline operator lsx::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator lsx::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator lsx::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<lsx::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<lsx::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::document_reference> : public lsx::implementation_simdjson_result_base<lsx::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(lsx::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator lsx::ondemand::array() & noexcept(false);
simdjson_inline operator lsx::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator lsx::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator lsx::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<lsx::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<lsx::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for lsx */
/* including simdjson/generic/ondemand/document_stream.h for lsx: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace lsx {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::document_stream> : public lsx::implementation_simdjson_result_base<lsx::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(lsx::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for lsx */
/* including simdjson/generic/ondemand/field.h for lsx: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::field> : public lsx::implementation_simdjson_result_base<lsx::ondemand::field> {
public:
simdjson_inline simdjson_result(lsx::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<lsx::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for lsx */
/* including simdjson/generic/ondemand/key_selector.h for lsx: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace lsx {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for lsx */
/* including simdjson/generic/ondemand/object.h for lsx: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::object> : public lsx::implementation_simdjson_result_base<lsx::ondemand::object> {
public:
simdjson_inline simdjson_result(lsx::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<lsx::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<lsx::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lsx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<lsx::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(lsx::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, lsx::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, lsx::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, lsx::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, lsx::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires lsx::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, lsx::ondemand::value>
simdjson_inline lsx::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, lsx::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires lsx::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(lsx::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline lsx::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(lsx::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(lsx::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline lsx::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(lsx::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for lsx */
/* including simdjson/generic/ondemand/object_iterator.h for lsx: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lsx::ondemand::object_iterator> : public lsx::implementation_simdjson_result_base<lsx::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(lsx::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<lsx::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<lsx::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<lsx::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<lsx::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for lsx */
/* including simdjson/generic/ondemand/ranges.h for lsx: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::lsx::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::lsx::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for lsx */
/* including simdjson/generic/ondemand/serialization.h for lsx: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, lsx::ondemand::document> &&
!std::same_as<T, lsx::ondemand::value> &&
!std::same_as<T, lsx::ondemand::object> &&
!std::same_as<T, lsx::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace lsx { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::object> x);
#endif
}}} // namespace simdjson::lsx::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for lsx */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for lsx: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for lsx */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
lsx::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, lsx::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
lsx::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, lsx::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
lsx::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, lsx::ondemand::value &val, T &out) noexcept(false) {
lsx::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, lsx::ondemand::document &doc, T &out) noexcept(false) {
lsx::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, lsx::ondemand::document_reference &doc, T &out) noexcept(false) {
lsx::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, lsx::ondemand::number>
&& !std::is_same_v<T, lsx::ondemand::document>
&& !std::is_same_v<T, lsx::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^lsx::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = lsx::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, lsx::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
lsx::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
lsx::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
lsx::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
lsx::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, lsx::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
lsx::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, lsx::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, lsx::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, lsx::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for lsx */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for lsx: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::array>::simdjson_result(
lsx::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<lsx::ondemand::array>(
std::forward<lsx::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<lsx::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<lsx::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lsx::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<lsx::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for lsx */
/* including simdjson/generic/ondemand/array_iterator-inl.h for lsx: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::array_iterator>::simdjson_result(
lsx::ondemand::array_iterator &&value
) noexcept
: lsx::implementation_simdjson_result_base<lsx::ondemand::array_iterator>(std::forward<lsx::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: lsx::implementation_simdjson_result_base<lsx::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<lsx::ondemand::array_iterator>::operator==(const simdjson_result<lsx::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<lsx::ondemand::array_iterator>::operator!=(const simdjson_result<lsx::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> &simdjson_result<lsx::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<lsx::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for lsx */
/* including simdjson/generic/ondemand/value-inl.h for lsx: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace lsx {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::value>::simdjson_result(
lsx::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<lsx::ondemand::value>(
std::forward<lsx::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<lsx::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<lsx::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<lsx::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lsx::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lsx::ondemand::array> simdjson_result<lsx::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<lsx::ondemand::object> simdjson_result<lsx::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lsx::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lsx::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lsx::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lsx::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<lsx::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<lsx::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<lsx::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<lsx::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<lsx::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<lsx::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<lsx::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<lsx::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<lsx::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<lsx::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<lsx::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<lsx::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lsx::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<lsx::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> simdjson_result<lsx::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<lsx::ondemand::value>::get<lsx::ondemand::value>(lsx::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<lsx::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<lsx::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::get<lsx::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<lsx::ondemand::json_type> simdjson_result<lsx::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<lsx::number_type> simdjson_result<lsx::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<lsx::ondemand::number> simdjson_result<lsx::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<lsx::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<lsx::ondemand::value>::operator lsx::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::value>::operator lsx::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::value>::operator lsx::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<lsx::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<lsx::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<lsx::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for lsx */
/* including simdjson/generic/ondemand/document-inl.h for lsx: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace lsx {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::document>::simdjson_result(
lsx::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<lsx::ondemand::document>(
std::forward<lsx::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<lsx::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<lsx::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<lsx::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lsx::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<lsx::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lsx::ondemand::array> simdjson_result<lsx::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<lsx::ondemand::object> simdjson_result<lsx::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lsx::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lsx::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lsx::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lsx::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<lsx::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<lsx::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<lsx::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<lsx::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<lsx::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<lsx::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<lsx::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<lsx::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<lsx::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<lsx::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<lsx::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<lsx::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lsx::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> simdjson_result<lsx::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<lsx::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<lsx::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<lsx::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<lsx::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<lsx::ondemand::document> simdjson_result<lsx::ondemand::document>::get<lsx::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<lsx::ondemand::document> simdjson_result<lsx::ondemand::document>::get<lsx::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<lsx::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document>::get<lsx::ondemand::document>(lsx::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document>::get<lsx::ondemand::document>(lsx::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<lsx::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<lsx::ondemand::json_type> simdjson_result<lsx::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<lsx::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<lsx::number_type> simdjson_result<lsx::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<lsx::ondemand::number> simdjson_result<lsx::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, lsx::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<lsx::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator lsx::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator lsx::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator lsx::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<lsx::ondemand::document>::operator lsx::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<lsx::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<lsx::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<lsx::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<lsx::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace lsx {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::simdjson_result(lsx::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<lsx::ondemand::document_reference>(std::forward<lsx::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<lsx::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lsx::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<lsx::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lsx::ondemand::array_iterator> simdjson_result<lsx::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lsx::ondemand::array> simdjson_result<lsx::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<lsx::ondemand::object> simdjson_result<lsx::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lsx::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lsx::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lsx::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lsx::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<lsx::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<lsx::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<lsx::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<lsx::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<lsx::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<lsx::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<lsx::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<lsx::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<lsx::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<lsx::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<lsx::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<lsx::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lsx::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> simdjson_result<lsx::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<lsx::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<lsx::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<lsx::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lsx::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<lsx::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<lsx::ondemand::json_type> simdjson_result<lsx::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document_reference>::get(lsx::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document_reference>::get(lsx::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<lsx::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<lsx::number_type> simdjson_result<lsx::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<lsx::ondemand::number> simdjson_result<lsx::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, lsx::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, lsx::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator lsx::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator lsx::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator lsx::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<lsx::ondemand::document_reference>::operator lsx::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<lsx::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<lsx::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for lsx */
/* including simdjson/generic/ondemand/document_stream-inl.h for lsx: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace lsx {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<lsx::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<lsx::ondemand::document_stream>::simdjson_result(
lsx::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<lsx::ondemand::document_stream>(
std::forward<lsx::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for lsx */
/* including simdjson/generic/ondemand/field-inl.h for lsx: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::field>::simdjson_result(
lsx::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<lsx::ondemand::field>(
std::forward<lsx::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<lsx::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<lsx::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string> simdjson_result<lsx::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lsx::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lsx::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lsx::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for lsx */
/* including simdjson/generic/ondemand/json_iterator-inl.h for lsx: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::json_iterator>::simdjson_result(lsx::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<lsx::ondemand::json_iterator>(std::forward<lsx::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<lsx::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lsx::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for lsx */
/* including simdjson/generic/ondemand/json_type-inl.h for lsx: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::json_type>::simdjson_result(lsx::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<lsx::ondemand::json_type>(std::forward<lsx::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<lsx::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lsx::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for lsx */
/* including simdjson/generic/ondemand/logger-inl.h for lsx: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace lsx {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for lsx */
/* including simdjson/generic/ondemand/object-inl.h for lsx: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::object>::simdjson_result(lsx::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<lsx::ondemand::object>(std::forward<lsx::ondemand::object>(value)) {}
simdjson_inline simdjson_result<lsx::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lsx::ondemand::object>(error) {}
simdjson_inline simdjson_result<lsx::ondemand::object_iterator> simdjson_result<lsx::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lsx::ondemand::object_iterator> simdjson_result<lsx::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<lsx::ondemand::object_iterator> simdjson_result<lsx::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<lsx::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<lsx::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<lsx::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lsx::ondemand::value> simdjson_result<lsx::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lsx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<lsx::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires lsx::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, lsx::ondemand::value>
simdjson_inline lsx::ondemand::for_each_result
simdjson_result<lsx::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, lsx::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires lsx::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(lsx::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline lsx::ondemand::for_each_result
simdjson_result<lsx::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(lsx::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(lsx::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline lsx::ondemand::for_each_result
simdjson_result<lsx::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(lsx::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<lsx::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<lsx::ondemand::object_position> simdjson_result<lsx::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<lsx::ondemand::object>::revert_position(lsx::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<lsx::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lsx::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lsx::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for lsx */
/* including simdjson/generic/ondemand/object_iterator-inl.h for lsx: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::object_iterator>::simdjson_result(
lsx::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<lsx::ondemand::object_iterator>(std::forward<lsx::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<lsx::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lsx::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<lsx::ondemand::field> simdjson_result<lsx::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<lsx::ondemand::object_iterator>::operator==(const simdjson_result<lsx::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<lsx::ondemand::object_iterator>::operator!=(const simdjson_result<lsx::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<lsx::ondemand::object_iterator> &simdjson_result<lsx::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for lsx */
/* including simdjson/generic/ondemand/ranges-inl.h for lsx: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace lsx {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::lsx::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::lsx::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::lsx::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::lsx::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::lsx::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::lsx::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for lsx */
/* including simdjson/generic/ondemand/parser-inl.h for lsx: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::parser>::simdjson_result(lsx::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<lsx::ondemand::parser>(std::forward<lsx::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<lsx::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lsx::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for lsx */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for lsx: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string>::simdjson_result(lsx::ondemand::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<lsx::ondemand::raw_json_string>(std::forward<lsx::ondemand::raw_json_string>(value)) {}
simdjson_inline simdjson_result<lsx::ondemand::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lsx::ondemand::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<lsx::ondemand::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline char simdjson_result<lsx::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
if (error()) { return error(); }
return first[i];
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<lsx::ondemand::raw_json_string>::unescape(lsx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<lsx::ondemand::raw_json_string>::unescape_wobbly(lsx::ondemand::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* end file simdjson/generic/ondemand/raw_json_string-inl.h for lsx */
/* including simdjson/generic/ondemand/token_iterator-inl.h for lsx: #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/token_iterator-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::token_iterator>::simdjson_result(lsx::ondemand::token_iterator &&value) noexcept
: implementation_simdjson_result_base<lsx::ondemand::token_iterator>(std::forward<lsx::ondemand::token_iterator>(value)) {}
simdjson_inline simdjson_result<lsx::ondemand::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lsx::ondemand::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/token_iterator-inl.h for lsx */
/* including simdjson/generic/ondemand/value_iterator-inl.h for lsx: #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/value_iterator-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lsx {
namespace ondemand {
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
bool escaping{false};
for (uint32_t i = 1; i < max_len; i++) {
switch (json[i]) {
case '"':
if (!escaping) { return true; }
escaping = false;
break;
case '\\':
escaping = !escaping;
break;
default:
escaping = false;
break;
}
}
return false;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
SIMDJSON_TRY(end_container());
return false;
}
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
// call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != '}') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
// If the last character is } *and* the first gibberish character is also '}'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed object.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
auto error = check_root_object();
if(error) { return error; }
return started_object();
}
simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
#if SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
return false;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return false;
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
// If someone queried a key but they not did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
//
// ```
// // At the beginning of a field
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // At the end of the object
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// ```
//
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
SIMDJSON_TRY(reset_object().get(has_value));
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
return raw_json_string(key);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
assert_at_next();
const uint8_t *k = _json_iter->return_current_and_advance();
if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
// After return_current_and_advance(), the current token is the ':' that follows
// the key. The closing quote sits just before it (only JSON whitespace may
// intervene), so step back from the ':' to the closing quote to get the length.
// In minified JSON this is a single back-step.
const char *q = reinterpret_cast<const char *>(_json_iter->peek());
do { --q; } while (*q != '"');
key = raw_json_string(k);
len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
assert_at_next();
if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
answer += std::string(" ]");
return answer;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
SIMDJSON_TRY( end_container() );
return false;
}
_json_iter->descend_to(depth()+1);
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
// also call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != ']') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
// If the last character is ] *and* the first gibberish character is also ']'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed array.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
auto error = check_root_array();
if (error) { return error; }
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
}
simdjson_inline bool value_iterator::is_root_negative() noexcept {
return numberparsing::is_negative(peek_root_scalar("numbersign"));
}
simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
return numberparsing::is_integer(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
return numberparsing::get_number_type(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
number num;
error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
if(error) { return error; }
return num;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<lsx::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_inline value_iterator value_iterator::child() const noexcept {
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool value_iterator::at_end() const noexcept {
return _json_iter->at_end();
}
simdjson_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.position() == start_position();
}
simdjson_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
return _json_iter->error;
}
simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
return _json_iter->string_buf_loc();
}
simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
return *_json_iter;
}
simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
return *_json_iter;
}
simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(start_position());
}
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
logger::log_start_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
const uint8_t *json;
if (!is_at_start()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
return SUCCESS;
}
simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
return _json_iter->peek();
}
simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
logger::log_error(*_json_iter, start_position(), depth(), message);
return INCORRECT_TYPE;
}
simdjson_inline bool value_iterator::is_at_start() const noexcept {
return position() == start_position();
}
simdjson_inline bool value_iterator::is_at_key() const noexcept {
// Keys are at the same depth as the object.
// Note here that we could be safer and check that we are within an object,
// but we do not.
//
// As long as we are at the object's depth, in a valid document,
// we will only ever be at { , : or the actual string key: ".
return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
}
simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = position() - start_position();
return delta == 1 || delta == 2;
}
inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_inline void value_iterator::move_at_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position);
}
simdjson_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
// Unlike reenter_child(), this does not require the live depth to be
// exactly one level shallower than depth, nor does it validate against
// the parser's per-depth container-start bookkeeping: neither holds in
// general for a caller-supplied snapshot (see object_position). What
// must still always hold, regardless of what was captured or how far
// the live iterator has since moved, is that position and depth are
// themselves sane values -- this is the same bound reenter_child()
// itself applies unconditionally.
SIMDJSON_ASSUME(position != nullptr);
SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
_json_iter->_depth = depth;
_json_iter->token.set_position(position);
}
simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_array();
}
simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_object();
}
inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return json_type::unknown;
}
}
simdjson_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lsx::ondemand::value_iterator>::simdjson_result(lsx::ondemand::value_iterator &&value) noexcept
: implementation_simdjson_result_base<lsx::ondemand::value_iterator>(std::forward<lsx::ondemand::value_iterator>(value)) {}
simdjson_inline simdjson_result<lsx::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lsx::ondemand::value_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/value_iterator-inl.h for lsx */
/* including simdjson/generic/ondemand/serialization-inl.h for lsx: #include "simdjson/generic/ondemand/serialization-inl.h" */
/* begin file simdjson/generic/ondemand/serialization-inl.h for lsx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-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/serialization.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace lsx::ondemand;
lsx::ondemand::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
lsx::ondemand::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
lsx::ondemand::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(lsx::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lsx::ondemand::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace lsx { namespace ondemand {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lsx::ondemand::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::lsx::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::lsx::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* end file simdjson/generic/ondemand/serialization-inl.h for lsx */
// JSON path accessor (compile-time) - must be after inline definitions
/* including simdjson/generic/ondemand/compile_time_accessors.h for lsx: #include "simdjson/generic/ondemand/compile_time_accessors.h" */
/* begin file simdjson/generic/ondemand/compile_time_accessors.h for lsx */
/**
* Compile-time JSON Path and JSON Pointer accessors using C++26 reflection (P2996)
*
* This file validates JSON paths/pointers against struct definitions at compile time
* and generates optimized accessor code with zero runtime overhead.
*
* ## How It Works
*
* **Compile Time**: Path is parsed, validated against struct, types are checked
* **Runtime**: Direct navigation with no parsing or validation overhead
*
* Example:
* ```cpp
* struct User { std::string name; std::vector<std::string> emails; };
*
* std::string email;
* path_accessor<User, ".emails[0]">::extract_field(doc, email);
*
* // Compile time validates:
* // 1. User has "emails" field
* // 2. "emails" is array-like
* // 3. Element type is std::string
* // 4. static_assert(^^std::string == ^^std::string)
*
* // Runtime just navigates:
* // doc.get_object().find_field("emails").get_array().at(0).get(email)
* ```
*
* ## Key Reflection APIs
*
* - `^^Type`: Reflect operator, converts type to std::meta::info
* - `std::meta::nonstatic_data_members_of(type)`: Get all fields of a struct
* - `std::meta::identifier_of(member)`: Get field name as string_view
* - `std::meta::type_of(member)`: Get reflected type of a field
* - `std::meta::is_array_type(type)`: Check if C-style array
* - `std::meta::remove_extent(array)`: Extract element type from array
* - `std::meta::members_of(type)`: Get all members including typedefs
* - `std::meta::is_type(member)`: Check if member is a type (vs field)
*
* All operations execute at compile time in consteval contexts.
*/
#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H */
/* amalgamation skipped (editor-only): */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
// is unlikely that we will have reflection support without concepts support.
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#include <string_view>
#include <cstddef>
#include <array>
namespace simdjson {
namespace lsx {
namespace ondemand {
/***
* JSONPath implementation for compile-time access
* RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
*/
namespace json_path {
// Note: value type must be fully defined before this header is included
// This is ensured by including this in amalgamated.h after value-inl.h
using ::simdjson::lsx::ondemand::value;
// Path step types
enum class step_type {
field, // .field_name or ["field_name"]
array_index // [index]
};
// Represents a single step in a JSON path
template<std::size_t N>
struct path_step {
step_type type;
char key[N]; // Field name (empty for array indices)
std::size_t index; // Array index (0 for field access)
constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
: type(t), index(idx) {
for (std::size_t i = 0; i < N; ++i) {
key[i] = k[i];
}
}
constexpr std::string_view key_view() const {
return {key, N - 1};
}
};
// Helper to create field step
template<std::size_t N>
consteval auto make_field_step(const char (&name)[N]) {
return path_step<N>(step_type::field, name, 0);
}
// Helper to create array index step
consteval auto make_index_step(std::size_t idx) {
return path_step<1>(step_type::array_index, "", idx);
}
// Parse state for compile-time JSON path parsing
struct parse_result {
bool success;
std::size_t pos;
std::string_view error_msg;
};
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
struct json_path_parser {
static constexpr std::string_view path_str = Path.view();
// Skip leading $ if present
static consteval std::size_t skip_root() {
if (!path_str.empty() && path_str[0] == '$') {
return 1;
}
return 0;
}
// Count the number of steps in the path at compile time
static consteval std::size_t count_steps() {
std::size_t count = 0;
std::size_t i = skip_root();
while (i < path_str.size()) {
if (path_str[i] == '.') {
// Field access: .field
++i;
if (i >= path_str.size()) break;
// Skip field name
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
++i;
}
++count;
} else if (path_str[i] == '[') {
// Array or bracket notation
++i;
if (i >= path_str.size()) break;
if (path_str[i] == '"' || path_str[i] == '\'') {
// Field access: ["field"] or ['field']
char quote = path_str[i];
++i;
while (i < path_str.size() && path_str[i] != quote) {
++i;
}
if (i < path_str.size()) ++i; // skip closing quote
if (i < path_str.size() && path_str[i] == ']') ++i;
} else {
// Array index: [0], [123]
while (i < path_str.size() && path_str[i] != ']') {
++i;
}
if (i < path_str.size()) ++i; // skip ]
}
++count;
} else {
++i;
}
}
return count;
}
// Parse a field name at compile time
static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
std::size_t len = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
out[len++] = path_str[i++];
}
out[len] = '\0';
return i;
}
// Parse an array index at compile time
static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
std::size_t index = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
index = index * 10 + (path_str[i] - '0');
++i;
}
return {i, index};
}
};
// Compile-time path accessor generator
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
using value = ::simdjson::lsx::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
// If T is a struct, validates the path at compile time
// If T is void, skips validation
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
// Validate path at compile time if T is a struct
if constexpr (std::is_class_v<T>) {
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
}
// Parse the path at compile time to build access steps
return access_impl<parser.skip_root()>(doc_or_val.get_value());
}
// Extract value at path directly into target with compile-time type validation
// Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
// Validate path exists in struct definition
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
// Get the type at the end of the path
constexpr auto final_type = get_final_type();
// Verify target type matches the field type
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
// All validation done at compile time - just navigate and extract
auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get the final type by walking the path through the struct type
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
// .field syntax
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) break;
if (path_view[i] == '"' || path_view[i] == '\'') {
// ["field"] syntax
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip quote
if (i < path_view.size() && path_view[i] == ']') ++i;
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else {
// [index] syntax - extract element type
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
current_type = get_element_type_reflected(current_type);
}
} else {
++i;
}
}
return current_type;
}
private:
// Walk path and extract directly into final field using compile-time reflection
template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
if (current.error()) return current.error();
// Base case: end of path - extract into target
if constexpr (PathPos >= path_view.size()) {
return current.get(target_ref);
}
// Field access: .field_name
else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
}
// Bracket notation: [index] or ["field"]
else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
constexpr auto elem_type = get_element_type_reflected(CurrentType);
static_assert(elem_type != ^^void, "Could not determine array element type");
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto elem_value = arr.at(index);
if constexpr (next_pos >= path_view.size()) {
return elem_value.get(target_ref);
} else {
return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
}
}
}
// Skip unexpected characters and continue
else {
return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
}
}
// Find member by name in reflected type
static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == name) {
return mem;
}
}
}
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if (current.error()) return current;
if constexpr (PathPos >= path_view.size()) {
return current;
} else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto next_value = arr.at(index);
return access_impl<next_pos>(next_value);
}
} else {
return access_impl<PathPos + 1>(current);
}
}
// Parse next field name
static consteval auto parse_next_field(std::size_t start) {
std::size_t i = start + 1;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
return std::make_tuple(field_name, i);
}
// Parse bracket notation: returns (is_field, field_name, next_pos, index)
static consteval auto parse_bracket(std::size_t start) {
std::size_t i = start + 1; // skip '['
if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
// Field access
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip closing quote
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(true, field_name, i, std::size_t(0));
} else {
// Array index
std::size_t index = 0;
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
index = index * 10 + (path_view[i] - '0');
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(false, std::string_view{}, i, index);
}
}
public:
// Check if reflected type is array-like (C-style array or indexable container)
// Uses reflection to test: 1) std::meta::is_array_type() for C arrays
// 2) std::meta::substitute() to test concepts::indexable_container concept
static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return true;
}
if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
}
return false;
}
// Extract element type from reflected array or container
// For C arrays: uses std::meta::remove_extent()
// For containers: finds value_type member using std::meta::members_of()
static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return std::meta::remove_extent(type_reflection);
}
auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::is_type(mem)) {
auto name = std::meta::identifier_of(mem);
if (name == "value_type") {
return mem;
}
}
}
return ^^void;
}
private:
// Check if type has member with given name
template<typename Type>
static consteval bool has_member(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return true;
}
}
return false;
}
// Get type of member by name
template<typename Type>
static consteval auto get_member_type(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return std::meta::type_of(mem);
}
}
return ^^void;
}
// Check if non-reflected type is array-like
template<typename Type>
static consteval bool is_container_type() {
using BaseType = std::remove_cvref_t<Type>;
if constexpr (requires { typename BaseType::value_type; }) {
return true;
}
if constexpr (std::is_array_v<BaseType>) {
return true;
}
return false;
}
// Extract element type from non-reflected container
template<typename Type>
using extract_element_type = std::conditional_t<
requires { typename std::remove_cvref_t<Type>::value_type; },
typename std::remove_cvref_t<Type>::value_type,
std::conditional_t<
std::is_array_v<std::remove_cvref_t<Type>>,
std::remove_extent_t<std::remove_cvref_t<Type>>,
void
>
>;
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) return false;
if (path_view[i] == '"' || path_view[i] == '\'') {
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i;
if (i < path_view.size() && path_view[i] == ']') ++i;
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else {
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
if (!is_array_like_reflected(current_type)) {
return false;
}
auto new_type = get_element_type_reflected(current_type);
if (new_type == ^^void) {
return false;
}
current_type = new_type;
}
} else {
++i;
}
}
return true;
}
};
// Compile-time path accessor with validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::lsx::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<T, Path>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::lsx::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<void, Path>;
return accessor::access(doc_or_val);
}
// ============================================================================
// JSON Pointer Compile-Time Support (RFC 6901)
// ============================================================================
// JSON Pointer parser: /field/0/nested (slash-separated)
template<constevalutil::fixed_string Pointer>
struct json_pointer_parser {
static constexpr std::string_view pointer_str = Pointer.view();
// Unescape token: ~0 -> ~, ~1 -> /
static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
out_len = 0;
for (std::size_t i = 0; i < src.size(); ++i) {
if (src[i] == '~' && i + 1 < src.size()) {
if (src[i + 1] == '0') {
dest[out_len++] = '~';
++i;
} else if (src[i + 1] == '1') {
dest[out_len++] = '/';
++i;
} else {
dest[out_len++] = src[i];
}
} else {
dest[out_len++] = src[i];
}
}
}
// Check if token is numeric
static consteval bool is_numeric(std::string_view token) {
if (token.empty()) return false;
if (token[0] == '0' && token.size() > 1) return false;
for (char c : token) {
if (c < '0' || c > '9') return false;
}
return true;
}
// Parse numeric token to index
static consteval std::size_t parse_index(std::string_view token) {
std::size_t result = 0;
for (char c : token) {
result = result * 10 + (c - '0');
}
return result;
}
// Count tokens in pointer
static consteval std::size_t count_tokens() {
if (pointer_str.empty() || pointer_str == "/") return 0;
std::size_t count = 0;
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
while (pos < pointer_str.size()) {
++count;
std::size_t next_slash = pointer_str.find('/', pos);
if (next_slash == std::string_view::npos) break;
pos = next_slash + 1;
}
return count;
}
// Get Nth token
static consteval std::string_view get_token(std::size_t token_index) {
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
std::size_t current_token = 0;
while (current_token < token_index) {
std::size_t next_slash = pointer_str.find('/', pos);
pos = next_slash + 1;
++current_token;
}
std::size_t token_end = pointer_str.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_str.size();
return pointer_str.substr(pos, token_end - pos);
}
};
// JSON Pointer accessor
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
using parser = json_pointer_parser<Pointer>;
static constexpr std::string_view pointer_view = Pointer.view();
static constexpr std::size_t token_count = parser::count_tokens();
// Validate pointer against struct definition
static consteval bool validate_pointer() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
// Extract token up to next /
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
return false;
}
current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
} else {
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) return false;
}
pos = token_end + 1;
}
return true;
}
// Recursive accessor
template<std::size_t TokenIndex>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if constexpr (TokenIndex >= token_count) {
return current;
} else {
constexpr std::string_view token = parser::get_token(TokenIndex);
if constexpr (parser::is_numeric(token)) {
constexpr std::size_t index = parser::parse_index(token);
auto arr = current.get_array().value_unsafe();
auto next_value = arr.at(index);
return access_impl<TokenIndex + 1>(next_value);
} else {
auto obj = current.get_object().value_unsafe();
auto next_value = obj.find_field_unordered(token);
return access_impl<TokenIndex + 1>(next_value);
}
}
}
// Access JSON value at pointer
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
if constexpr (std::is_class_v<T>) {
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
}
if (pointer_view.empty() || pointer_view == "/") {
if constexpr (requires { doc_or_val.get_value(); }) {
return doc_or_val.get_value();
} else {
return doc_or_val;
}
}
simdjson_result<value> current = doc_or_val.get_value();
return access_impl<0>(current);
}
// Extract value at pointer directly into target with type validation
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
constexpr auto final_type = get_final_type();
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
simdjson_result<value> current_value = doc_or_val.get_value();
auto json_value = access_impl<0>(current_value);
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get final type by walking pointer through struct
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
} else {
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
break;
}
}
}
pos = token_end + 1;
}
return current_type;
}
};
// Compile-time JSON Pointer accessor with validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::lsx::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<T, Pointer>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::lsx::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<void, Pointer>;
return accessor::access(doc_or_val);
}
} // namespace json_path
} // namespace ondemand
} // namespace lsx
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* end file simdjson/generic/ondemand/compile_time_accessors.h for lsx */
/* end file simdjson/generic/ondemand/amalgamated.h for lsx */
/* including simdjson/lsx/end.h: #include "simdjson/lsx/end.h" */
/* begin file simdjson/lsx/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lsx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "lsx" */
#undef SIMDJSON_IMPLEMENTATION
/* end file simdjson/lsx/end.h */
#endif // SIMDJSON_LSX_ONDEMAND_H
/* end file simdjson/lsx/ondemand.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
/* including simdjson/lasx/ondemand.h: #include "simdjson/lasx/ondemand.h" */
/* begin file simdjson/lasx/ondemand.h */
#ifndef SIMDJSON_LASX_ONDEMAND_H
#define SIMDJSON_LASX_ONDEMAND_H
/* including simdjson/lasx/begin.h: #include "simdjson/lasx/begin.h" */
/* begin file simdjson/lasx/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "lasx" */
#define SIMDJSON_IMPLEMENTATION lasx
#include <lsxintrin.h> // This is a hack. We should not need to put this include here.
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
// nothing needed.
#else
SIMDJSON_TARGET_REGION("lasx,lsx")
#endif
/* including simdjson/lasx/base.h: #include "simdjson/lasx/base.h" */
/* begin file simdjson/lasx/base.h */
#ifndef SIMDJSON_LASX_BASE_H
#define SIMDJSON_LASX_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Implementation for LASX.
*/
namespace lasx {
class implementation;
namespace {
namespace simd {
template <typename T> struct simd8;
template <typename T> struct simd8x64;
} // namespace simd
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_BASE_H
/* end file simdjson/lasx/base.h */
/* including simdjson/lasx/intrinsics.h: #include "simdjson/lasx/intrinsics.h" */
/* begin file simdjson/lasx/intrinsics.h */
#ifndef SIMDJSON_LASX_INTRINSICS_H
#define SIMDJSON_LASX_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <lsxintrin.h>
#include <lasxintrin.h>
static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for LoongArch ASX");
#endif // SIMDJSON_LASX_INTRINSICS_H
/* end file simdjson/lasx/intrinsics.h */
/* including simdjson/lasx/bitmanipulation.h: #include "simdjson/lasx/bitmanipulation.h" */
/* begin file simdjson/lasx/bitmanipulation.h */
#ifndef SIMDJSON_LASX_BITMANIPULATION_H
#define SIMDJSON_LASX_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmask.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline int count_ones(uint64_t input_num) {
return __lasx_xvpickve2gr_w(__lasx_xvpcnt_d(__m256i(v4u64{input_num, 0, 0, 0})), 0);
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_BITMANIPULATION_H
/* end file simdjson/lasx/bitmanipulation.h */
/* including simdjson/lasx/bitmask.h: #include "simdjson/lasx/bitmask.h" */
/* begin file simdjson/lasx/bitmask.h */
#ifndef SIMDJSON_LASX_BITMASK_H
#define SIMDJSON_LASX_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
return bitmask;
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif
/* end file simdjson/lasx/bitmask.h */
/* including simdjson/lasx/numberparsing_defs.h: #include "simdjson/lasx/numberparsing_defs.h" */
/* begin file simdjson/lasx/numberparsing_defs.h */
#ifndef SIMDJSON_LASX_NUMBERPARSING_DEFS_H
#define SIMDJSON_LASX_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/intrinsics.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
namespace simdjson {
namespace lasx {
namespace numberparsing {
// we don't have appropriate instructions, so let us use a scalar function
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
uint64_t val;
std::memcpy(&val, chars, sizeof(uint64_t));
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace lasx
} // namespace simdjson
#ifndef SIMDJSON_SWAR_NUMBER_PARSING
#if SIMDJSON_IS_BIG_ENDIAN
#define SIMDJSON_SWAR_NUMBER_PARSING 0
#else
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif
#endif
#endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H
/* end file simdjson/lasx/numberparsing_defs.h */
/* including simdjson/lasx/simd.h: #include "simdjson/lasx/simd.h" */
/* begin file simdjson/lasx/simd.h */
#ifndef SIMDJSON_LASX_SIMD_H
#define SIMDJSON_LASX_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
namespace simd {
// Forward-declared so they can be used by splat and friends.
template<typename Child>
struct base {
__m256i value;
// Zero constructor
simdjson_inline base() : value{__m256i()} {}
// Conversion from SIMD register
simdjson_inline base(const __m256i _value) : value(_value) {}
// Conversion to SIMD register
simdjson_inline operator const __m256i&() const { return this->value; }
simdjson_inline operator __m256i&() { return this->value; }
simdjson_inline operator const v32i8&() const { return (v32i8&)this->value; }
simdjson_inline operator v32i8&() { return (v32i8&)this->value; }
// Bit operations
simdjson_inline Child operator|(const Child other) const { return __lasx_xvor_v(*this, other); }
simdjson_inline Child operator&(const Child other) const { return __lasx_xvand_v(*this, other); }
simdjson_inline Child operator^(const Child other) const { return __lasx_xvxor_v(*this, other); }
simdjson_inline Child bit_andnot(const Child other) const { return __lasx_xvandn_v(other, *this); }
simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Forward-declared so they can be used by splat and friends.
template<typename T>
struct simd8;
template<typename T, typename Mask=simd8<bool>>
struct base8: base<simd8<T>> {
simdjson_inline base8() : base<simd8<T>>() {}
simdjson_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
friend simdjson_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return __lasx_xvseq_b(lhs, rhs); }
static const int SIZE = sizeof(base<simd8<T>>::value);
template<int N=1>
simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
__m256i hi = __lasx_xvbsll_v(*this, N);
__m256i lo = __lasx_xvbsrl_v(*this, 16 - N);
__m256i tmp = __lasx_xvbsrl_v(prev_chunk, 16 - N);
lo = __lasx_xvpermi_q(lo, tmp, 0x21);
return __lasx_xvor_v(hi, lo);
}
};
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool>: base8<bool> {
static simdjson_inline simd8<bool> splat(bool _value) { return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); }
simdjson_inline simd8() : base8() {}
simdjson_inline simd8(const __m256i _value) : base8<bool>(_value) {}
// Splat constructor
simdjson_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
simdjson_inline int to_bitmask() const {
__m256i mask = __lasx_xvmskltz_b(*this);
return (__lasx_xvpickve2gr_w(mask, 4) << 16) | (__lasx_xvpickve2gr_w(mask, 0));
}
simdjson_inline bool any() const {
__m256i v = __lasx_xvmsknz_b(*this);
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
};
template<typename T>
struct base8_numeric: base8<T> {
static simdjson_inline simd8<T> splat(T _value) {
return __lasx_xvreplgr2vr_b(_value);
}
static simdjson_inline simd8<T> zero() { return __lasx_xvldi(0); }
static simdjson_inline simd8<T> load(const T values[32]) {
return __lasx_xvld(reinterpret_cast<const __m256i *>(values), 0);
}
// Repeat 16 values as many times as necessary (usually for lookup tables)
static simdjson_inline simd8<T> repeat_16(
T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
) {
return simd8<T>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
simdjson_inline base8_numeric() : base8<T>() {}
simdjson_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
// Store to array
simdjson_inline void store(T dst[32]) const {
return __lasx_xvst(*this, reinterpret_cast<__m256i *>(dst), 0);
}
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<T> operator+(const simd8<T> other) const { return __lasx_xvadd_b(*this, other); }
simdjson_inline simd8<T> operator-(const simd8<T> other) const { return __lasx_xvsub_b(*this, other); }
simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
// Override to distinguish from bool version
simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __lasx_xvshuf_b(lookup_table, lookup_table, *this);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
// get written.
template<typename L>
simdjson_inline void compress(uint32_t mask, L * output) const {
using internal::thintable_epi8;
using internal::BitsSetTable256mul2;
using internal::pshufb_combine_table;
// this particular implementation was inspired by haswell
// lasx do it in 4 steps, first 8 bytes and then second 8 bytes...
uint8_t mask1 = uint8_t(mask); // least significant 8 bits
uint8_t mask2 = uint8_t(mask >> 8); // second significant 8 bits
uint8_t mask3 = uint8_t(mask >> 16); // ...
uint8_t mask4 = uint8_t(mask >> 24); // ...
// next line just loads the 64-bit values thintable_epi8[mask{1,2,3,4}]
// into a 256-bit register.
__m256i shufmask = {int64_t(thintable_epi8[mask1]), int64_t(thintable_epi8[mask2]) + 0x0808080808080808, int64_t(thintable_epi8[mask3]), int64_t(thintable_epi8[mask4]) + 0x0808080808080808};
// this is the version "nearly pruned"
__m256i pruned = __lasx_xvshuf_b(*this, *this, shufmask);
// we still need to put the pieces back together.
// we compute the popcount of the first words:
int pop1 = BitsSetTable256mul2[mask1];
int pop2 = BitsSetTable256mul2[mask2];
int pop3 = BitsSetTable256mul2[mask3];
// then load the corresponding mask
__m256i masklo = __lasx_xvldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop1 * 8);
__m256i maskhi = __lasx_xvldx(reinterpret_cast<void*>(reinterpret_cast<unsigned long>(pshufb_combine_table)), pop3 * 8);
__m256i compactmask = __lasx_xvpermi_q(maskhi, masklo, 0x20);
__m256i answer = __lasx_xvshuf_b(pruned, pruned, compactmask);
__lasx_xvst(answer, reinterpret_cast<uint8_t*>(output), 0);
uint64_t value3 = __lasx_xvpickve2gr_du(answer, 2);
uint64_t value4 = __lasx_xvpickve2gr_du(answer, 3);
uint64_t *pos = reinterpret_cast<uint64_t*>(reinterpret_cast<uint8_t*>(output) + 16 - (pop1 + pop2) / 2);
pos[0] = value3;
pos[1] = value4;
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> : base8_numeric<int8_t> {
simdjson_inline simd8() : base8_numeric<int8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const int8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31
) : simd8({
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return __lasx_xvmax_b(*this, other); }
simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return __lasx_xvmin_b(*this, other); }
simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return __lasx_xvslt_b(other, *this); }
simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return __lasx_xvslt_b(*this, other); }
};
// Unsigned bytes
template<>
struct simd8<uint8_t>: base8_numeric<uint8_t> {
simdjson_inline simd8() : base8_numeric<uint8_t>() {}
simdjson_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
// Splat constructor
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
// Array constructor
simdjson_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
// Member-by-member initialization
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31
) : simd8(__m256i(v32u8{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v16,v17,v18,v19,v20,v21,v22,v23,
v24,v25,v26,v27,v28,v29,v30,v31
})) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15,
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __lasx_xvsadd_bu(*this, other); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __lasx_xvssub_bu(*this, other); }
// Order-specific operations
simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return __lasx_xvmax_bu(*this, other); }
simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return __lasx_xvmin_bu(other, *this); }
// Same as >, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
// Same as <, but only guarantees true is nonzero (< guarantees true = -1)
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
// Bit-specific operations
simdjson_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
simdjson_inline bool is_ascii() const {
__m256i mask = __lasx_xvmskltz_b(*this);
return (0 == __lasx_xvpickve2gr_w(mask, 0)) && (0 == __lasx_xvpickve2gr_w(mask, 4));
}
simdjson_inline bool bits_not_set_anywhere() const {
__m256i v = __lasx_xvmsknz_b(*this);
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
__m256i v = __lasx_xvmsknz_b(__lasx_xvand_v(*this, bits));
return (0 == __lasx_xvpickve2gr_w(v, 0)) && (0 == __lasx_xvpickve2gr_w(v, 4));
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(__lasx_xvsrli_b(*this, N)); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(__lasx_xvslli_b(*this, N)); }
};
template<typename T>
struct simd8x64 {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
static_assert(NUM_CHUNKS == 2, "LASX kernel should use two registers per 64-byte block.");
const simd8<T> chunks[NUM_CHUNKS];
template<int idx> simd8<uint8_t> get() const { return idx < NUM_CHUNKS ? chunks[idx] : simd8<T>(); }
simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+32)} {}
simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
uint32_t mask1 = uint32_t(mask);
uint32_t mask2 = uint32_t(mask >> 32);
__m256i zcnt = __lasx_xvpcnt_w(__m256i(v4u64{~mask, 0, 0, 0}));
uint64_t zcnt1 = __lasx_xvpickve2gr_wu(zcnt, 0);
uint64_t zcnt2 = __lasx_xvpickve2gr_wu(zcnt, 1);
// There should be a critical value which processes in scaler is faster.
if (zcnt1)
this->chunks[0].compress(mask1, output);
if (zcnt2)
this->chunks[1].compress(mask2, output + zcnt1);
return zcnt1 + zcnt2;
}
simdjson_inline void store(T ptr[64]) const {
this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
this->chunks[1].store(ptr+sizeof(simd8<T>)*1);
}
simdjson_inline uint64_t to_bitmask() const {
__m256i mask0 = __lasx_xvmskltz_b(this->chunks[0]);
__m256i mask1 = __lasx_xvmskltz_b(this->chunks[1]);
__m256i mask_tmp = __lasx_xvpickve_w(mask0, 4);
__m256i tmp = __lasx_xvpickve_w(mask1, 4);
mask0 = __lasx_xvinsve0_w(mask0, mask1, 1);
mask_tmp = __lasx_xvinsve0_w(mask_tmp, tmp, 1);
return __lasx_xvpickve2gr_du(__lasx_xvpackev_h(mask_tmp, mask0), 0);
}
simdjson_inline simd8<T> reduce_or() const {
return this->chunks[0] | this->chunks[1];
}
simdjson_inline uint64_t eq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] == mask,
this->chunks[1] == mask
).to_bitmask();
}
simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
return simd8x64<bool>(
this->chunks[0] == other.chunks[0],
this->chunks[1] == other.chunks[1]
).to_bitmask();
}
simdjson_inline uint64_t lteq(const T m) const {
const simd8<T> mask = simd8<T>::splat(m);
return simd8x64<bool>(
this->chunks[0] <= mask,
this->chunks[1] <= mask
).to_bitmask();
}
}; // struct simd8x64<T>
} // namespace simd
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_SIMD_H
/* end file simdjson/lasx/simd.h */
/* including simdjson/lasx/stringparsing_defs.h: #include "simdjson/lasx/stringparsing_defs.h" */
/* begin file simdjson/lasx/stringparsing_defs.h */
#ifndef SIMDJSON_LASX_STRINGPARSING_DEFS_H
#define SIMDJSON_LASX_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/simd.h" */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return bs_bits != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint32_t bs_bits;
uint32_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return {
static_cast<uint32_t>((v == '\\').to_bitmask()), // bs_bits
static_cast<uint32_t>((v == '"').to_bitmask()), // quote_bits
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
static_cast<uint64_t>((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_LASX_STRINGPARSING_DEFS_H
/* end file simdjson/lasx/stringparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/lasx/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for lasx: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for lasx */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for lasx: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::lasx::number_type */
using number_type = simdjson::lasx::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for lasx */
/* including simdjson/generic/ondemand/deserialize.h for lasx: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for lasx */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<lasx::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<lasx::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<lasx::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<lasx::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = lasx::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = lasx::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = lasx::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = lasx::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = lasx::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = lasx::ondemand::array;
using object_type = lasx::ondemand::object;
using value_type = lasx::ondemand::value;
using document_type = lasx::ondemand::document;
using document_reference_type = lasx::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for lasx */
/* including simdjson/generic/ondemand/value_iterator.h for lasx: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::value_iterator> : public lasx::implementation_simdjson_result_base<lasx::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(lasx::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for lasx */
/* including simdjson/generic/ondemand/value.h for lasx: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::value> : public lasx::implementation_simdjson_result_base<lasx::ondemand::value> {
public:
simdjson_inline simdjson_result(lasx::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<lasx::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator lasx::ondemand::array() noexcept(false);
simdjson_inline operator lasx::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator lasx::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<lasx::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<lasx::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<lasx::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<lasx::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<lasx::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<lasx::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<lasx::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<lasx::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<lasx::ondemand::value>::get<lasx::ondemand::value>(
lasx::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<lasx::ondemand::value>
simdjson_result<lasx::ondemand::value>::get<lasx::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for lasx */
/* including simdjson/generic/ondemand/logger.h for lasx: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for lasx */
/* including simdjson/generic/ondemand/token_iterator.h for lasx: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::token_iterator> : public lasx::implementation_simdjson_result_base<lasx::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(lasx::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for lasx */
/* including simdjson/generic/ondemand/json_iterator.h for lasx: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::json_iterator> : public lasx::implementation_simdjson_result_base<lasx::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(lasx::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for lasx */
/* including simdjson/generic/ondemand/json_type.h for lasx: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::json_type> : public lasx::implementation_simdjson_result_base<lasx::ondemand::json_type> {
public:
simdjson_inline simdjson_result(lasx::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for lasx */
/* including simdjson/generic/ondemand/raw_json_string.h for lasx: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::raw_json_string> : public lasx::implementation_simdjson_result_base<lasx::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(lasx::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(lasx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(lasx::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for lasx */
/* including simdjson/generic/ondemand/parser.h for lasx: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::parser> : public lasx::implementation_simdjson_result_base<lasx::ondemand::parser> {
public:
simdjson_inline simdjson_result(lasx::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for lasx */
// All other declarations
/* including simdjson/generic/ondemand/array.h for lasx: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::array> : public lasx::implementation_simdjson_result_base<lasx::ondemand::array> {
public:
simdjson_inline simdjson_result(lasx::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, lasx::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, lasx::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, lasx::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, lasx::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for lasx */
/* including simdjson/generic/ondemand/array_iterator.h for lasx: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::array_iterator> : public lasx::implementation_simdjson_result_base<lasx::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<lasx::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(lasx::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<lasx::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<lasx::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<lasx::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for lasx */
/* including simdjson/generic/ondemand/document.h for lasx: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::document> : public lasx::implementation_simdjson_result_base<lasx::ondemand::document> {
public:
simdjson_inline simdjson_result(lasx::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using lasx::implementation_simdjson_result_base<lasx::ondemand::document>::operator*;
using lasx::implementation_simdjson_result_base<lasx::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, lasx::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator lasx::ondemand::array() & noexcept(false);
simdjson_inline operator lasx::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator lasx::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator lasx::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<lasx::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<lasx::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::document_reference> : public lasx::implementation_simdjson_result_base<lasx::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(lasx::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator lasx::ondemand::array() & noexcept(false);
simdjson_inline operator lasx::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator lasx::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator lasx::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<lasx::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<lasx::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for lasx */
/* including simdjson/generic/ondemand/document_stream.h for lasx: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace lasx {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::document_stream> : public lasx::implementation_simdjson_result_base<lasx::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(lasx::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for lasx */
/* including simdjson/generic/ondemand/field.h for lasx: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::field> : public lasx::implementation_simdjson_result_base<lasx::ondemand::field> {
public:
simdjson_inline simdjson_result(lasx::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<lasx::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for lasx */
/* including simdjson/generic/ondemand/key_selector.h for lasx: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace lasx {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for lasx */
/* including simdjson/generic/ondemand/object.h for lasx: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::object> : public lasx::implementation_simdjson_result_base<lasx::ondemand::object> {
public:
simdjson_inline simdjson_result(lasx::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<lasx::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<lasx::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<lasx::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<lasx::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(lasx::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, lasx::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, lasx::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, lasx::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, lasx::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires lasx::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, lasx::ondemand::value>
simdjson_inline lasx::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, lasx::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires lasx::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(lasx::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline lasx::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(lasx::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(lasx::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline lasx::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(lasx::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for lasx */
/* including simdjson/generic/ondemand/object_iterator.h for lasx: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<lasx::ondemand::object_iterator> : public lasx::implementation_simdjson_result_base<lasx::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(lasx::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<lasx::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<lasx::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<lasx::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<lasx::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for lasx */
/* including simdjson/generic/ondemand/ranges.h for lasx: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::lasx::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::lasx::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for lasx */
/* including simdjson/generic/ondemand/serialization.h for lasx: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, lasx::ondemand::document> &&
!std::same_as<T, lasx::ondemand::value> &&
!std::same_as<T, lasx::ondemand::object> &&
!std::same_as<T, lasx::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace lasx { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::object> x);
#endif
}}} // namespace simdjson::lasx::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for lasx */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for lasx: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for lasx */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
lasx::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, lasx::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
lasx::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, lasx::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
lasx::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, lasx::ondemand::value &val, T &out) noexcept(false) {
lasx::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, lasx::ondemand::document &doc, T &out) noexcept(false) {
lasx::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, lasx::ondemand::document_reference &doc, T &out) noexcept(false) {
lasx::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, lasx::ondemand::number>
&& !std::is_same_v<T, lasx::ondemand::document>
&& !std::is_same_v<T, lasx::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^lasx::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = lasx::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, lasx::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
lasx::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
lasx::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
lasx::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
lasx::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, lasx::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
lasx::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, lasx::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, lasx::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, lasx::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for lasx */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for lasx: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::array>::simdjson_result(
lasx::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<lasx::ondemand::array>(
std::forward<lasx::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<lasx::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<lasx::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lasx::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<lasx::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for lasx */
/* including simdjson/generic/ondemand/array_iterator-inl.h for lasx: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::array_iterator>::simdjson_result(
lasx::ondemand::array_iterator &&value
) noexcept
: lasx::implementation_simdjson_result_base<lasx::ondemand::array_iterator>(std::forward<lasx::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: lasx::implementation_simdjson_result_base<lasx::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<lasx::ondemand::array_iterator>::operator==(const simdjson_result<lasx::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<lasx::ondemand::array_iterator>::operator!=(const simdjson_result<lasx::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> &simdjson_result<lasx::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<lasx::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for lasx */
/* including simdjson/generic/ondemand/value-inl.h for lasx: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace lasx {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::value>::simdjson_result(
lasx::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<lasx::ondemand::value>(
std::forward<lasx::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<lasx::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<lasx::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<lasx::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lasx::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lasx::ondemand::array> simdjson_result<lasx::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<lasx::ondemand::object> simdjson_result<lasx::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lasx::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lasx::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lasx::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lasx::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<lasx::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<lasx::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<lasx::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<lasx::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<lasx::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<lasx::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<lasx::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<lasx::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<lasx::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<lasx::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<lasx::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<lasx::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lasx::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<lasx::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> simdjson_result<lasx::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<lasx::ondemand::value>::get<lasx::ondemand::value>(lasx::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<lasx::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<lasx::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::get<lasx::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<lasx::ondemand::json_type> simdjson_result<lasx::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<lasx::number_type> simdjson_result<lasx::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<lasx::ondemand::number> simdjson_result<lasx::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<lasx::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<lasx::ondemand::value>::operator lasx::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::value>::operator lasx::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::value>::operator lasx::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<lasx::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<lasx::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<lasx::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for lasx */
/* including simdjson/generic/ondemand/document-inl.h for lasx: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace lasx {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::document>::simdjson_result(
lasx::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<lasx::ondemand::document>(
std::forward<lasx::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<lasx::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<lasx::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<lasx::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lasx::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<lasx::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lasx::ondemand::array> simdjson_result<lasx::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<lasx::ondemand::object> simdjson_result<lasx::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lasx::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lasx::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lasx::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lasx::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<lasx::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<lasx::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<lasx::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<lasx::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<lasx::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<lasx::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<lasx::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<lasx::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<lasx::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<lasx::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<lasx::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<lasx::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lasx::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> simdjson_result<lasx::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<lasx::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<lasx::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<lasx::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<lasx::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<lasx::ondemand::document> simdjson_result<lasx::ondemand::document>::get<lasx::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<lasx::ondemand::document> simdjson_result<lasx::ondemand::document>::get<lasx::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<lasx::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document>::get<lasx::ondemand::document>(lasx::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document>::get<lasx::ondemand::document>(lasx::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<lasx::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<lasx::ondemand::json_type> simdjson_result<lasx::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<lasx::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<lasx::number_type> simdjson_result<lasx::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<lasx::ondemand::number> simdjson_result<lasx::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, lasx::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<lasx::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator lasx::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator lasx::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator lasx::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<lasx::ondemand::document>::operator lasx::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<lasx::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<lasx::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<lasx::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<lasx::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace lasx {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::simdjson_result(lasx::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<lasx::ondemand::document_reference>(std::forward<lasx::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<lasx::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lasx::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<lasx::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lasx::ondemand::array_iterator> simdjson_result<lasx::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lasx::ondemand::array> simdjson_result<lasx::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<lasx::ondemand::object> simdjson_result<lasx::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lasx::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<lasx::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lasx::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<lasx::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<lasx::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<lasx::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<lasx::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<lasx::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<lasx::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<lasx::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<lasx::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<lasx::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<lasx::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<lasx::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<lasx::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<lasx::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lasx::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> simdjson_result<lasx::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<lasx::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<lasx::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<lasx::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, lasx::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<lasx::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<lasx::ondemand::json_type> simdjson_result<lasx::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document_reference>::get(lasx::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document_reference>::get(lasx::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<lasx::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<lasx::number_type> simdjson_result<lasx::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<lasx::ondemand::number> simdjson_result<lasx::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, lasx::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, lasx::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator lasx::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator lasx::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator lasx::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<lasx::ondemand::document_reference>::operator lasx::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<lasx::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<lasx::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for lasx */
/* including simdjson/generic/ondemand/document_stream-inl.h for lasx: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace lasx {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<lasx::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<lasx::ondemand::document_stream>::simdjson_result(
lasx::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<lasx::ondemand::document_stream>(
std::forward<lasx::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for lasx */
/* including simdjson/generic/ondemand/field-inl.h for lasx: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::field>::simdjson_result(
lasx::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<lasx::ondemand::field>(
std::forward<lasx::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<lasx::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<lasx::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string> simdjson_result<lasx::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lasx::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<lasx::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<lasx::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for lasx */
/* including simdjson/generic/ondemand/json_iterator-inl.h for lasx: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::json_iterator>::simdjson_result(lasx::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<lasx::ondemand::json_iterator>(std::forward<lasx::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<lasx::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lasx::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for lasx */
/* including simdjson/generic/ondemand/json_type-inl.h for lasx: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::json_type>::simdjson_result(lasx::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<lasx::ondemand::json_type>(std::forward<lasx::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<lasx::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lasx::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for lasx */
/* including simdjson/generic/ondemand/logger-inl.h for lasx: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace lasx {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for lasx */
/* including simdjson/generic/ondemand/object-inl.h for lasx: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::object>::simdjson_result(lasx::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<lasx::ondemand::object>(std::forward<lasx::ondemand::object>(value)) {}
simdjson_inline simdjson_result<lasx::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lasx::ondemand::object>(error) {}
simdjson_inline simdjson_result<lasx::ondemand::object_iterator> simdjson_result<lasx::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<lasx::ondemand::object_iterator> simdjson_result<lasx::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<lasx::ondemand::object_iterator> simdjson_result<lasx::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<lasx::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<lasx::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<lasx::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<lasx::ondemand::value> simdjson_result<lasx::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, lasx::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<lasx::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires lasx::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, lasx::ondemand::value>
simdjson_inline lasx::ondemand::for_each_result
simdjson_result<lasx::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, lasx::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires lasx::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(lasx::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline lasx::ondemand::for_each_result
simdjson_result<lasx::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(lasx::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(lasx::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline lasx::ondemand::for_each_result
simdjson_result<lasx::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(lasx::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<lasx::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<lasx::ondemand::object_position> simdjson_result<lasx::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<lasx::ondemand::object>::revert_position(lasx::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<lasx::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<lasx::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<lasx::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for lasx */
/* including simdjson/generic/ondemand/object_iterator-inl.h for lasx: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::object_iterator>::simdjson_result(
lasx::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<lasx::ondemand::object_iterator>(std::forward<lasx::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<lasx::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lasx::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<lasx::ondemand::field> simdjson_result<lasx::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<lasx::ondemand::object_iterator>::operator==(const simdjson_result<lasx::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<lasx::ondemand::object_iterator>::operator!=(const simdjson_result<lasx::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<lasx::ondemand::object_iterator> &simdjson_result<lasx::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for lasx */
/* including simdjson/generic/ondemand/ranges-inl.h for lasx: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace lasx {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::lasx::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::lasx::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::lasx::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::lasx::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::lasx::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::lasx::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for lasx */
/* including simdjson/generic/ondemand/parser-inl.h for lasx: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::parser>::simdjson_result(lasx::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<lasx::ondemand::parser>(std::forward<lasx::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<lasx::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lasx::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for lasx */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for lasx: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept {
return iter.unescape_wobbly(*this);
}
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept {
bool in_escape = false;
const char *s = str.raw();
while (true) {
switch (*s) {
case '\\': in_escape = !in_escape; break;
case '"': if (in_escape) { in_escape = false; } else { return out; } break;
default: if (in_escape) { in_escape = false; }
}
out << *s;
s++;
}
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string>::simdjson_result(lasx::ondemand::raw_json_string &&value) noexcept
: implementation_simdjson_result_base<lasx::ondemand::raw_json_string>(std::forward<lasx::ondemand::raw_json_string>(value)) {}
simdjson_inline simdjson_result<lasx::ondemand::raw_json_string>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lasx::ondemand::raw_json_string>(error) {}
simdjson_inline simdjson_result<const char *> simdjson_result<lasx::ondemand::raw_json_string>::raw() const noexcept {
if (error()) { return error(); }
return first.raw();
}
simdjson_inline char simdjson_result<lasx::ondemand::raw_json_string>::operator[](size_t i) const noexcept {
if (error()) { return error(); }
return first[i];
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<lasx::ondemand::raw_json_string>::unescape(lasx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept {
if (error()) { return error(); }
return first.unescape(iter, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> simdjson_result<lasx::ondemand::raw_json_string>::unescape_wobbly(lasx::ondemand::json_iterator &iter) const noexcept {
if (error()) { return error(); }
return first.unescape_wobbly(iter);
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* end file simdjson/generic/ondemand/raw_json_string-inl.h for lasx */
/* including simdjson/generic/ondemand/token_iterator-inl.h for lasx: #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/token_iterator-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
simdjson_inline token_iterator::token_iterator(
const uint8_t *_buf,
token_position position
) noexcept : buf{_buf}, _position{position}
{
}
simdjson_inline uint32_t token_iterator::current_offset() const noexcept {
return *(_position);
}
simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept {
return &buf[*(_position++)];
}
simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept {
return &buf[*position];
}
simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept {
return *position;
}
simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept {
return *(position+1) - *position;
}
simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept {
return *(position+2) - *(position) > *(position+1) - *(position) ?
*(position+1) - *(position)
: *(position+2) - *(position);
}
simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept {
return &buf[*(_position+delta)];
}
simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept {
return *(_position+delta);
}
simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept {
return *(_position+delta+1) - *(_position+delta);
}
simdjson_inline token_position token_iterator::position() const noexcept {
return _position;
}
simdjson_inline void token_iterator::set_position(token_position target_position) noexcept {
_position = target_position;
}
simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept {
return _position == other._position;
}
simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept {
return _position != other._position;
}
simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept {
return _position > other._position;
}
simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept {
return _position >= other._position;
}
simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept {
return _position < other._position;
}
simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept {
return _position <= other._position;
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::token_iterator>::simdjson_result(lasx::ondemand::token_iterator &&value) noexcept
: implementation_simdjson_result_base<lasx::ondemand::token_iterator>(std::forward<lasx::ondemand::token_iterator>(value)) {}
simdjson_inline simdjson_result<lasx::ondemand::token_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lasx::ondemand::token_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/token_iterator-inl.h for lasx */
/* including simdjson/generic/ondemand/value_iterator-inl.h for lasx: #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/value_iterator-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace lasx {
namespace ondemand {
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
bool escaping{false};
for (uint32_t i = 1; i < max_len; i++) {
switch (json[i]) {
case '"':
if (!escaping) { return true; }
escaping = false;
break;
case '\\':
escaping = !escaping;
break;
default:
escaping = false;
break;
}
}
return false;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline value_iterator::value_iterator(
json_iterator *json_iter,
depth_t depth,
token_position start_position
) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
{
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
SIMDJSON_TRY( start_container('{', "Not an object", "object") );
return started_root_object();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
assert_at_container_start();
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
if (*_json_iter->peek() == '}') {
logger::log_value(*_json_iter, "empty object");
_json_iter->return_current_and_advance();
SIMDJSON_TRY(end_container());
return false;
}
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
// call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != '}') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
}
// If the last character is } *and* the first gibberish character is also '}'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed object.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
auto error = check_root_object();
if(error) { return error; }
return started_object();
}
simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
#if SIMDJSON_CHECK_EOF
if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
// if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
_json_iter->ascend_to(depth()-1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
assert_at_next();
// It's illegal to call this unless there are more tokens: anything that ends in } or ] is
// obligated to verify there are more tokens if they are not the top level.
switch (*_json_iter->return_current_and_advance()) {
case '}':
logger::log_end_value(*_json_iter, "object");
SIMDJSON_TRY( end_container() );
return false;
case ',':
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between object fields");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
error_code error;
bool has_value;
//
// Initially, the object can be in one of a few different places:
//
// 1. The start of the object, at the first field:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
if (at_first_field()) {
has_value = true;
//
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
return false;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
if ((error = skip_child() )) { abandon(); return error; }
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
while (has_value) {
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
//if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() ); // Skip the value entirely
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// If the loop ended, we're out of fields to look at.
return false;
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
/**
* When find_field_unordered_raw is called, we can either be pointing at the
* first key, pointing outside (at the closing brace) or if a key was matched
* we can be either pointing right afterthe ':' right before the value (that we need skip),
* or we may have consumed the value and we might be at a comma or at the
* final brace (ready for a call to has_next_field()).
*/
error_code error;
bool has_value;
// First, we scan from that point to the end.
// If we don't find a match, we may loop back around, and scan from the beginning to that point.
token_position search_start = _json_iter->position();
// We want to know whether we need to go back to the beginning.
bool at_first = at_first_field();
///////////////
// Initially, the object can be in one of a few different places:
//
// 1. At the first key:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2, index 1)
// ```
//
if (at_first) {
has_value = true;
// 2. When a previous search did not yield a value or the object is empty:
//
// ```
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// { }
// ^ (depth 0, index 2)
// ```
//
} else if (!is_open()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
// If we're past the end of the object, we're being iterated out of order.
// Note: this is not perfect detection. It's possible the user is inside some other object; if so,
// this object iterator will blithely scan that object for fields.
if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
#endif
SIMDJSON_TRY(reset_object().get(has_value));
at_first = true;
// 3. When a previous search found a field or an iterator yielded a value:
//
// ```
// // When a field was not fully consumed (or not even touched at all)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 2)
// // When a field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // When the last field was fully consumed
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// ```
//
} else {
// If someone queried a key but they not did access the value, then we are left pointing
// at the ':' and we need to move forward through the value... If the value was
// processed then skip_child() does not move the iterator (but may adjust the depth).
if ((error = skip_child() )) { abandon(); return error; }
search_start = _json_iter->position();
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
#if SIMDJSON_DEVELOPMENT_CHECKS
if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
#endif
}
// After initial processing, we will be in one of two states:
//
// ```
// // At the beginning of a field
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 1)
// // At the end of the object
// { "a": [ 1, 2 ], "b": [ 3, 4 ] }
// ^ (depth 0)
// ```
//
// Next, we find a match starting from the current position.
while (has_value) {
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
if ((error = field_key().get(actual_key) )) { abandon(); return error; };
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
if ((error = field_value() )) { abandon(); return error; }
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
}
// Performance note: it maybe wasteful to rewind to the beginning when there might be
// no other query following. Indeed, it would require reskipping the whole object.
// Instead, you can just stay where you are. If there is a new query, there is always time
// to rewind.
if(at_first) { return false; }
// If we reach the end without finding a match, search the rest of the fields starting at the
// beginning of the object.
// (We have already run through the object before, so we've already validated its structure. We
// don't check errors in this bit.)
SIMDJSON_TRY(reset_object().get(has_value));
while (true) {
SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
// Get the key and colon, stopping at the value.
raw_json_string actual_key;
// size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
// Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
// field_key() advances the pointer and checks that '"' is found (corresponding to a key).
// The depth is left unchanged by field_key().
error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
// field_value() will advance and check that we find a ':' separating the
// key and the value. It will also increment the depth by one.
error = field_value(); SIMDJSON_ASSUME(!error);
// If it matches, stop and return
// We could do it this way if we wanted to allow arbitrary
// key content (including escaped quotes).
// if (actual_key.unsafe_is_equal(max_key_length, key)) {
// Instead we do the following which may trigger buffer overruns if the
// user provides an adversarial key (containing a well placed unescaped quote
// character and being longer than the number of bytes remaining in the JSON
// input).
if (actual_key.unsafe_is_equal(key)) {
logger::log_event(*this, "match", key, -2);
// If we return here, then we return while pointing at the ':' that we just checked.
return true;
}
// No match: skip the value and see if , or } is next
logger::log_event(*this, "no match", key, -2);
// The call to skip_child is meant to skip over the value corresponding to the key.
// After skip_child(), we are right before the next comma (',') or the final brace ('}').
SIMDJSON_TRY( skip_child() );
// If we reached the end of the key-value pair we started from, then we know
// that the key is not there so we return false. We are either right before
// the next comma or the final brace.
if(_json_iter->position() == search_start) { return false; }
// The has_next_field() advances the pointer and check that either ',' or '}' is found.
// It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
// then we are in error and we abort.
error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
// If we make the mistake of exiting here, then we could be left pointing at a key
// in the middle of an object. That's not an allowable state.
}
// If the loop ended, we're out of fields to look at. The program should
// never reach this point.
return false;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
assert_at_next();
const uint8_t *key = _json_iter->return_current_and_advance();
if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
return raw_json_string(key);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
assert_at_next();
const uint8_t *k = _json_iter->return_current_and_advance();
if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
// After return_current_and_advance(), the current token is the ':' that follows
// the key. The closing quote sits just before it (only JSON whitespace may
// intervene), so step back from the ':' to the closing quote to get the length.
// In minified JSON this is a single back-step.
const char *q = reinterpret_cast<const char *>(_json_iter->peek());
do { --q; } while (*q != '"');
key = raw_json_string(k);
len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
return SUCCESS;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
assert_at_next();
if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
_json_iter->descend_to(depth()+1);
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
SIMDJSON_TRY( start_container('[', "Not an array", "array") );
return started_root_array();
}
inline std::string value_iterator::to_string() const noexcept {
auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
answer += std::string(" ]");
return answer;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
assert_at_container_start();
if (*_json_iter->peek() == ']') {
logger::log_value(*_json_iter, "empty array");
_json_iter->return_current_and_advance();
SIMDJSON_TRY( end_container() );
return false;
}
_json_iter->descend_to(depth()+1);
#if SIMDJSON_DEVELOPMENT_CHECKS
_json_iter->set_start_position(_depth, start_position());
#endif
return true;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
// When in streaming mode, we cannot expect peek_last() to be the last structural element of the
// current document. It only works in the normal mode where we have indexed a single document.
// Note that adding a check for 'streaming' is not expensive since we only have at most
// one root element.
if ( ! _json_iter->streaming() ) {
// The following lines do not fully protect against garbage content within the
// array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
// also call `at_end()` on the document instance at the end of the processing to
// ensure that the processing has finished at the end.
//
if (*_json_iter->peek_last() != ']') {
_json_iter->abandon();
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
}
// If the last character is ] *and* the first gibberish character is also ']'
// then on-demand could accidentally go over. So we need additional checks.
// https://github.com/simdjson/simdjson/issues/1834
// Checking that the document is balanced requires a full scan which is potentially
// expensive, but it only happens in edge cases where the first padding character is
// a closing bracket.
if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
_json_iter->abandon();
// The exact error would require more work. It will typically be an unclosed array.
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
}
}
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
auto error = check_root_array();
if (error) { return error; }
return started_array();
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
assert_at_next();
logger::log_event(*this, "has_next_element");
switch (*_json_iter->return_current_and_advance()) {
case ']':
logger::log_end_value(*_json_iter, "array");
SIMDJSON_TRY( end_container() );
return false;
case ',':
_json_iter->descend_to(depth()+1);
return true;
default:
return report_error(TAPE_ERROR, "Missing comma between array elements");
}
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
auto not_true = atomparsing::str4ncmp(json, "true");
auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
if (error) { return incorrect_type_error("Not a boolean"); }
return simdjson_result<bool>(!not_true);
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
// if we start with 'n', we must be a null
if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
return is_null_string;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
return get_raw_json_string().unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_string(allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
return get_raw_json_string().unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
auto result = parse_bool(peek_non_root_scalar("bool"));
if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
return result;
}
simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
bool is_null_value;
SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
if(is_null_value) { advance_non_root_scalar("null"); }
return is_null_value;
}
simdjson_inline bool value_iterator::is_negative() noexcept {
return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
}
simdjson_inline bool value_iterator::is_root_negative() noexcept {
return numberparsing::is_negative(peek_root_scalar("numbersign"));
}
simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
return numberparsing::is_integer(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
return numberparsing::get_number_type(peek_non_root_scalar("integer"));
}
simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
number num;
error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
if(error) { return error; }
return num;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("is_root_integer");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
return false; // if there are more than 20 characters, it cannot be represented as an integer.
}
auto answer = numberparsing::is_integer(tmpbuf);
// If the parsing was a success, we must still check that it is
// a single scalar. Note that we parse first because of cases like '[]' where
// getting TRAILING_CONTENT is wrong.
if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<lasx::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return number_type::big_integer;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
auto answer = numberparsing::get_number_type(tmpbuf);
if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
return answer;
}
simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("number");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
// NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
uint8_t tmpbuf[1074+8+1+1];
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
if(numberparsing::check_if_integer(json, max_len)) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
return BIGINT_ERROR;
}
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
return NUMBER_ERROR;
}
number num;
error_code error = numberparsing::parse_number(tmpbuf, num);
if(error) { return error; }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("number");
return num;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
}
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
std::string_view content;
// Save the string buffer location so that we can restore it after get_string
auto saved_string_buf_loc = _json_iter->string_buf_loc();
auto err = get_root_string(check_trailing, allow_replacement).get(content);
if (err) { return err; }
internal::assign_utf8(receiver, content);
// Restore the string buffer location, effectively discarding any temporary string storage
_json_iter->string_buf_loc() = saved_string_buf_loc;
return SUCCESS;
}
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
}
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
auto json = peek_scalar("string");
if (*json != '"') { return incorrect_type_error("Not a string"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
if (_json_iter->allow_incomplete_json()) {
const size_t remaining_input_length = _json_iter->remaining_input_length(json);
const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
if (!raw_json_string_is_quote_terminated(json, max_len)) {
return STRING_ERROR;
}
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
advance_scalar("string");
return raw_json_string(json+1);
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("uint64");
uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("uint64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("int64");
uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_integer_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("int64");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("double");
// Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
// 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
// number: -0.<fraction>e-308.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_double_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("double");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
// We use the same buffer size as get_root_double: the number of significant
// digits that matter is smaller for binary32, but the JSON document may still
// spell out a long number that we must parse (and round) faithfully.
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("float");
uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
return NUMBER_ERROR;
}
auto result = numberparsing::parse_float_in_string(tmpbuf);
if(result.error() == SUCCESS) {
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("float");
}
return result;
}
simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("bool");
// We have a boolean if we have either "true" or "false" and the next character is either
// a structural character or whitespace. We also check that the length is correct:
// "true" and "false" are 4 and 5 characters long, respectively.
bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
(max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("bool");
return value_true;
}
simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
auto max_len = peek_root_length();
auto json = peek_root_scalar("null");
bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
(max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
if(result) { // we have something that looks like a null.
if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
advance_root_scalar("null");
} else if (json[0] == 'n') {
return incorrect_type_error("Not a null but starts with n");
}
return result;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
return _json_iter->skip_child(depth());
}
simdjson_inline value_iterator value_iterator::child() const noexcept {
assert_at_child();
return { _json_iter, depth()+1, _json_iter->token.position() };
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
// marked non-inline.
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline bool value_iterator::is_open() const noexcept {
return _json_iter->depth() >= depth();
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool value_iterator::at_end() const noexcept {
return _json_iter->at_end();
}
simdjson_inline bool value_iterator::at_start() const noexcept {
return _json_iter->token.position() == start_position();
}
simdjson_inline bool value_iterator::at_first_field() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
return _json_iter->token.position() == start_position() + 1;
}
simdjson_inline void value_iterator::abandon() noexcept {
_json_iter->abandon();
}
simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
return _depth;
}
simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
return _json_iter->error;
}
simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
return _json_iter->string_buf_loc();
}
simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
return *_json_iter;
}
simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
return *_json_iter;
}
simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
return _json_iter->peek(start_position());
}
simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
return _json_iter->peek_length(start_position());
}
simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
return _json_iter->peek_root_length(start_position());
}
simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return peek_start(); }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
if (!is_at_start()) { return; }
// Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
assert_at_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
logger::log_start_value(*_json_iter, start_position(), depth(), type);
// If we're not at the position anymore, we don't want to advance the cursor.
const uint8_t *json;
if (!is_at_start()) {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
#endif
json = peek_start();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
} else {
assert_at_start();
/**
* We should be prudent. Let us peek. If it is not the right type, we
* return an error. Only once we have determined that we have the right
* type are we allowed to advance!
*/
json = _json_iter->peek();
if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
_json_iter->return_current_and_advance();
}
return SUCCESS;
}
simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_root();
return _json_iter->peek();
}
simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return peek_start(); }
assert_at_non_root_start();
return _json_iter->peek();
}
simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_root();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
logger::log_value(*_json_iter, start_position(), depth(), type);
if (!is_at_start()) { return; }
assert_at_non_root_start();
_json_iter->return_current_and_advance();
_json_iter->ascend_to(depth()-1);
}
simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
logger::log_error(*_json_iter, start_position(), depth(), message);
return INCORRECT_TYPE;
}
simdjson_inline bool value_iterator::is_at_start() const noexcept {
return position() == start_position();
}
simdjson_inline bool value_iterator::is_at_key() const noexcept {
// Keys are at the same depth as the object.
// Note here that we could be safer and check that we are within an object,
// but we do not.
//
// As long as we are at the object's depth, in a valid document,
// we will only ever be at { , : or the actual string key: ".
return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
}
simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
// We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
auto delta = position() - start_position();
return delta == 1 || delta == 2;
}
inline void value_iterator::assert_at_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_container_start() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_next() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth );
SIMDJSON_ASSUME( _depth > 0 );
}
simdjson_inline void value_iterator::move_at_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position);
}
simdjson_inline void value_iterator::move_at_container_start() noexcept {
_json_iter->_depth = _depth;
_json_iter->token.set_position(_start_position + 1);
}
simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
// Unlike reenter_child(), this does not require the live depth to be
// exactly one level shallower than depth, nor does it validate against
// the parser's per-depth container-start bookkeeping: neither holds in
// general for a caller-supplied snapshot (see object_position). What
// must still always hold, regardless of what was captured or how far
// the live iterator has since moved, is that position and depth are
// themselves sane values -- this is the same bound reenter_child()
// itself applies unconditionally.
SIMDJSON_ASSUME(position != nullptr);
SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
_json_iter->_depth = depth;
_json_iter->token.set_position(position);
}
simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_array();
}
simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
if(error()) { return error(); }
move_at_container_start();
return started_object();
}
inline void value_iterator::assert_at_child() const noexcept {
SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
SIMDJSON_ASSUME( _depth > 0 );
}
inline void value_iterator::assert_at_root() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth == 1 );
}
inline void value_iterator::assert_at_non_root_start() const noexcept {
assert_at_start();
SIMDJSON_ASSUME( _depth > 1 );
}
inline void value_iterator::assert_is_valid() const noexcept {
SIMDJSON_ASSUME( _json_iter != nullptr );
}
simdjson_inline bool value_iterator::is_valid() const noexcept {
return _json_iter != nullptr;
}
simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
switch (*peek_start()) {
case '{':
return json_type::object;
case '[':
return json_type::array;
case '"':
return json_type::string;
case 'n':
return json_type::null;
case 't': case 'f':
return json_type::boolean;
case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return json_type::number;
default:
return json_type::unknown;
}
}
simdjson_inline token_position value_iterator::start_position() const noexcept {
return _start_position;
}
simdjson_inline token_position value_iterator::position() const noexcept {
return _json_iter->position();
}
simdjson_inline token_position value_iterator::end_position() const noexcept {
return _json_iter->end_position();
}
simdjson_inline token_position value_iterator::last_position() const noexcept {
return _json_iter->last_position();
}
simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
return _json_iter->report_error(error, message);
}
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<lasx::ondemand::value_iterator>::simdjson_result(lasx::ondemand::value_iterator &&value) noexcept
: implementation_simdjson_result_base<lasx::ondemand::value_iterator>(std::forward<lasx::ondemand::value_iterator>(value)) {}
simdjson_inline simdjson_result<lasx::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<lasx::ondemand::value_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/value_iterator-inl.h for lasx */
/* including simdjson/generic/ondemand/serialization-inl.h for lasx: #include "simdjson/generic/ondemand/serialization-inl.h" */
/* begin file simdjson/generic/ondemand/serialization-inl.h for lasx */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-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/serialization.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/builder/json_builder.h" */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
inline std::string_view trim(const std::string_view str) noexcept {
// We can almost surely do better by rolling our own find_first_not_of function.
size_t first = str.find_first_not_of(" \t\n\r");
// If we have the empty string (just white space), then no trimming is possible, and
// we return the empty string_view.
if (std::string_view::npos == first) { return std::string_view(); }
size_t last = str.find_last_not_of(" \t\n\r");
return str.substr(first, (last - first + 1));
}
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::document& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::document_reference& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::value& x) noexcept {
/**
* If we somehow receive a value that has already been consumed,
* then the following code could be in trouble. E.g., we create
* an array as needed, but if an array was already created, then
* it could be bad.
*/
using namespace lasx::ondemand;
lasx::ondemand::json_type t;
auto error = x.type().get(t);
if(error != SUCCESS) { return error; }
switch (t)
{
case json_type::array:
{
lasx::ondemand::array array;
error = x.get_array().get(array);
if(error) { return error; }
return to_json_string(array);
}
case json_type::object:
{
lasx::ondemand::object object;
error = x.get_object().get(object);
if(error) { return error; }
return to_json_string(object);
}
default:
return trim(x.raw_json_token());
}
}
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::object& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(lasx::ondemand::array& x) noexcept {
std::string_view v;
auto error = x.raw_json().get(v);
if(error) {return error; }
return trim(v);
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::document> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::document_reference> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::value> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::object> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
inline simdjson_result<std::string_view> to_json_string(simdjson_result<lasx::ondemand::array> x) {
if (x.error()) { return x.error(); }
return to_json_string(x.value_unsafe());
}
} // namespace simdjson
namespace simdjson { namespace lasx { namespace ondemand {
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::value> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::value x) {
std::string_view v;
auto error = simdjson::to_json_string(x).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::array> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::array value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document_reference& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::document>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::document_reference>&& x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document& value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
throw simdjson::simdjson_error(error);
}
}
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::lasx::ondemand::object> x) {
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
return (out << x.value());
}
#else
inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::object value) {
std::string_view v;
auto error = simdjson::to_json_string(value).get(v);
if(error == simdjson::SUCCESS) {
return (out << v);
} else {
return (out << error);
}
}
#endif
}}} // namespace simdjson::lasx::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H
/* end file simdjson/generic/ondemand/serialization-inl.h for lasx */
// JSON path accessor (compile-time) - must be after inline definitions
/* including simdjson/generic/ondemand/compile_time_accessors.h for lasx: #include "simdjson/generic/ondemand/compile_time_accessors.h" */
/* begin file simdjson/generic/ondemand/compile_time_accessors.h for lasx */
/**
* Compile-time JSON Path and JSON Pointer accessors using C++26 reflection (P2996)
*
* This file validates JSON paths/pointers against struct definitions at compile time
* and generates optimized accessor code with zero runtime overhead.
*
* ## How It Works
*
* **Compile Time**: Path is parsed, validated against struct, types are checked
* **Runtime**: Direct navigation with no parsing or validation overhead
*
* Example:
* ```cpp
* struct User { std::string name; std::vector<std::string> emails; };
*
* std::string email;
* path_accessor<User, ".emails[0]">::extract_field(doc, email);
*
* // Compile time validates:
* // 1. User has "emails" field
* // 2. "emails" is array-like
* // 3. Element type is std::string
* // 4. static_assert(^^std::string == ^^std::string)
*
* // Runtime just navigates:
* // doc.get_object().find_field("emails").get_array().at(0).get(email)
* ```
*
* ## Key Reflection APIs
*
* - `^^Type`: Reflect operator, converts type to std::meta::info
* - `std::meta::nonstatic_data_members_of(type)`: Get all fields of a struct
* - `std::meta::identifier_of(member)`: Get field name as string_view
* - `std::meta::type_of(member)`: Get reflected type of a field
* - `std::meta::is_array_type(type)`: Check if C-style array
* - `std::meta::remove_extent(array)`: Extract element type from array
* - `std::meta::members_of(type)`: Get all members including typedefs
* - `std::meta::is_type(member)`: Check if member is a type (vs field)
*
* All operations execute at compile time in consteval contexts.
*/
#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H */
/* amalgamation skipped (editor-only): */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
// is unlikely that we will have reflection support without concepts support.
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#include <string_view>
#include <cstddef>
#include <array>
namespace simdjson {
namespace lasx {
namespace ondemand {
/***
* JSONPath implementation for compile-time access
* RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
*/
namespace json_path {
// Note: value type must be fully defined before this header is included
// This is ensured by including this in amalgamated.h after value-inl.h
using ::simdjson::lasx::ondemand::value;
// Path step types
enum class step_type {
field, // .field_name or ["field_name"]
array_index // [index]
};
// Represents a single step in a JSON path
template<std::size_t N>
struct path_step {
step_type type;
char key[N]; // Field name (empty for array indices)
std::size_t index; // Array index (0 for field access)
constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
: type(t), index(idx) {
for (std::size_t i = 0; i < N; ++i) {
key[i] = k[i];
}
}
constexpr std::string_view key_view() const {
return {key, N - 1};
}
};
// Helper to create field step
template<std::size_t N>
consteval auto make_field_step(const char (&name)[N]) {
return path_step<N>(step_type::field, name, 0);
}
// Helper to create array index step
consteval auto make_index_step(std::size_t idx) {
return path_step<1>(step_type::array_index, "", idx);
}
// Parse state for compile-time JSON path parsing
struct parse_result {
bool success;
std::size_t pos;
std::string_view error_msg;
};
// Compile-time JSON path parser
// Supports subset: .field, ["field"], [index], nested combinations
template<constevalutil::fixed_string Path>
struct json_path_parser {
static constexpr std::string_view path_str = Path.view();
// Skip leading $ if present
static consteval std::size_t skip_root() {
if (!path_str.empty() && path_str[0] == '$') {
return 1;
}
return 0;
}
// Count the number of steps in the path at compile time
static consteval std::size_t count_steps() {
std::size_t count = 0;
std::size_t i = skip_root();
while (i < path_str.size()) {
if (path_str[i] == '.') {
// Field access: .field
++i;
if (i >= path_str.size()) break;
// Skip field name
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
++i;
}
++count;
} else if (path_str[i] == '[') {
// Array or bracket notation
++i;
if (i >= path_str.size()) break;
if (path_str[i] == '"' || path_str[i] == '\'') {
// Field access: ["field"] or ['field']
char quote = path_str[i];
++i;
while (i < path_str.size() && path_str[i] != quote) {
++i;
}
if (i < path_str.size()) ++i; // skip closing quote
if (i < path_str.size() && path_str[i] == ']') ++i;
} else {
// Array index: [0], [123]
while (i < path_str.size() && path_str[i] != ']') {
++i;
}
if (i < path_str.size()) ++i; // skip ]
}
++count;
} else {
++i;
}
}
return count;
}
// Parse a field name at compile time
static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
std::size_t len = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
out[len++] = path_str[i++];
}
out[len] = '\0';
return i;
}
// Parse an array index at compile time
static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
std::size_t index = 0;
std::size_t i = start;
while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
index = index * 10 + (path_str[i] - '0');
++i;
}
return {i, index};
}
};
// Compile-time path accessor generator
template<typename T, constevalutil::fixed_string Path>
struct path_accessor {
using value = ::simdjson::lasx::ondemand::value;
static constexpr auto parser = json_path_parser<Path>();
static constexpr std::size_t num_steps = parser.count_steps();
static constexpr std::string_view path_view = Path.view();
// Compile-time accessor generation
// If T is a struct, validates the path at compile time
// If T is void, skips validation
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
// Validate path at compile time if T is a struct
if constexpr (std::is_class_v<T>) {
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
}
// Parse the path at compile time to build access steps
return access_impl<parser.skip_root()>(doc_or_val.get_value());
}
// Extract value at path directly into target with compile-time type validation
// Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
// Validate path exists in struct definition
constexpr bool path_valid = validate_path();
static_assert(path_valid, "JSON path does not match struct definition");
// Get the type at the end of the path
constexpr auto final_type = get_final_type();
// Verify target type matches the field type
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
// All validation done at compile time - just navigate and extract
auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get the final type by walking the path through the struct type
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
// .field syntax
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) break;
if (path_view[i] == '"' || path_view[i] == '\'') {
// ["field"] syntax
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip quote
if (i < path_view.size() && path_view[i] == ']') ++i;
auto members = std::meta::nonstatic_data_members_of(
current_type, std::meta::access_context::unchecked()
);
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
break;
}
}
} else {
// [index] syntax - extract element type
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
current_type = get_element_type_reflected(current_type);
}
} else {
++i;
}
}
return current_type;
}
private:
// Walk path and extract directly into final field using compile-time reflection
template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
if (current.error()) return current.error();
// Base case: end of path - extract into target
if constexpr (PathPos >= path_view.size()) {
return current.get(target_ref);
}
// Field access: .field_name
else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
}
// Bracket notation: [index] or ["field"]
else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
constexpr auto member_info = find_member_by_name(CurrentType, field_name);
static_assert(member_info != ^^void, "Field not found in struct");
constexpr auto member_type = std::meta::type_of(member_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto field_value = obj.find_field_unordered(field_name);
if constexpr (next_pos >= path_view.size()) {
return field_value.get(target_ref);
} else {
return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
}
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
constexpr auto elem_type = get_element_type_reflected(CurrentType);
static_assert(elem_type != ^^void, "Could not determine array element type");
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto elem_value = arr.at(index);
if constexpr (next_pos >= path_view.size()) {
return elem_value.get(target_ref);
} else {
return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
}
}
}
// Skip unexpected characters and continue
else {
return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
}
}
// Find member by name in reflected type
static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == name) {
return mem;
}
}
}
// Generate compile-time accessor code by walking the path
template<std::size_t PathPos>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if (current.error()) return current;
if constexpr (PathPos >= path_view.size()) {
return current;
} else if constexpr (path_view[PathPos] == '.') {
constexpr auto field_info = parse_next_field(PathPos);
constexpr std::string_view field_name = std::get<0>(field_info);
constexpr std::size_t next_pos = std::get<1>(field_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else if constexpr (path_view[PathPos] == '[') {
constexpr auto bracket_info = parse_bracket(PathPos);
constexpr bool is_field = std::get<0>(bracket_info);
constexpr std::size_t next_pos = std::get<2>(bracket_info);
if constexpr (is_field) {
constexpr std::string_view field_name = std::get<1>(bracket_info);
auto obj_result = current.get_object();
if (obj_result.error()) return obj_result.error();
auto obj = obj_result.value_unsafe();
auto next_value = obj.find_field_unordered(field_name);
return access_impl<next_pos>(next_value);
} else {
constexpr std::size_t index = std::get<3>(bracket_info);
auto arr_result = current.get_array();
if (arr_result.error()) return arr_result.error();
auto arr = arr_result.value_unsafe();
auto next_value = arr.at(index);
return access_impl<next_pos>(next_value);
}
} else {
return access_impl<PathPos + 1>(current);
}
}
// Parse next field name
static consteval auto parse_next_field(std::size_t start) {
std::size_t i = start + 1;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
return std::make_tuple(field_name, i);
}
// Parse bracket notation: returns (is_field, field_name, next_pos, index)
static consteval auto parse_bracket(std::size_t start) {
std::size_t i = start + 1; // skip '['
if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
// Field access
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i; // skip closing quote
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(true, field_name, i, std::size_t(0));
} else {
// Array index
std::size_t index = 0;
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
index = index * 10 + (path_view[i] - '0');
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
return std::make_tuple(false, std::string_view{}, i, index);
}
}
public:
// Check if reflected type is array-like (C-style array or indexable container)
// Uses reflection to test: 1) std::meta::is_array_type() for C arrays
// 2) std::meta::substitute() to test concepts::indexable_container concept
static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return true;
}
if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
}
return false;
}
// Extract element type from reflected array or container
// For C arrays: uses std::meta::remove_extent()
// For containers: finds value_type member using std::meta::members_of()
static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
if (std::meta::is_array_type(type_reflection)) {
return std::meta::remove_extent(type_reflection);
}
auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::is_type(mem)) {
auto name = std::meta::identifier_of(mem);
if (name == "value_type") {
return mem;
}
}
}
return ^^void;
}
private:
// Check if type has member with given name
template<typename Type>
static consteval bool has_member(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return true;
}
}
return false;
}
// Get type of member by name
template<typename Type>
static consteval auto get_member_type(std::string_view member_name) {
constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == member_name) {
return std::meta::type_of(mem);
}
}
return ^^void;
}
// Check if non-reflected type is array-like
template<typename Type>
static consteval bool is_container_type() {
using BaseType = std::remove_cvref_t<Type>;
if constexpr (requires { typename BaseType::value_type; }) {
return true;
}
if constexpr (std::is_array_v<BaseType>) {
return true;
}
return false;
}
// Extract element type from non-reflected container
template<typename Type>
using extract_element_type = std::conditional_t<
requires { typename std::remove_cvref_t<Type>::value_type; },
typename std::remove_cvref_t<Type>::value_type,
std::conditional_t<
std::is_array_v<std::remove_cvref_t<Type>>,
std::remove_extent_t<std::remove_cvref_t<Type>>,
void
>
>;
// Validate path matches struct definition
static consteval bool validate_path() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t i = parser.skip_root();
while (i < path_view.size()) {
if (path_view[i] == '.') {
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else if (path_view[i] == '[') {
++i;
if (i >= path_view.size()) return false;
if (path_view[i] == '"' || path_view[i] == '\'') {
char quote = path_view[i];
++i;
std::size_t field_start = i;
while (i < path_view.size() && path_view[i] != quote) {
++i;
}
std::string_view field_name = path_view.substr(field_start, i - field_start);
if (i < path_view.size()) ++i;
if (i < path_view.size() && path_view[i] == ']') ++i;
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == field_name) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) {
return false;
}
} else {
while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
++i;
}
if (i < path_view.size() && path_view[i] == ']') ++i;
if (!is_array_like_reflected(current_type)) {
return false;
}
auto new_type = get_element_type_reflected(current_type);
if (new_type == ^^void) {
return false;
}
current_type = new_type;
}
} else {
++i;
}
}
return true;
}
};
// Compile-time path accessor with validation
template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::lasx::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<T, Path>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Path, typename DocOrValue>
inline simdjson_result<::simdjson::lasx::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = path_accessor<void, Path>;
return accessor::access(doc_or_val);
}
// ============================================================================
// JSON Pointer Compile-Time Support (RFC 6901)
// ============================================================================
// JSON Pointer parser: /field/0/nested (slash-separated)
template<constevalutil::fixed_string Pointer>
struct json_pointer_parser {
static constexpr std::string_view pointer_str = Pointer.view();
// Unescape token: ~0 -> ~, ~1 -> /
static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
out_len = 0;
for (std::size_t i = 0; i < src.size(); ++i) {
if (src[i] == '~' && i + 1 < src.size()) {
if (src[i + 1] == '0') {
dest[out_len++] = '~';
++i;
} else if (src[i + 1] == '1') {
dest[out_len++] = '/';
++i;
} else {
dest[out_len++] = src[i];
}
} else {
dest[out_len++] = src[i];
}
}
}
// Check if token is numeric
static consteval bool is_numeric(std::string_view token) {
if (token.empty()) return false;
if (token[0] == '0' && token.size() > 1) return false;
for (char c : token) {
if (c < '0' || c > '9') return false;
}
return true;
}
// Parse numeric token to index
static consteval std::size_t parse_index(std::string_view token) {
std::size_t result = 0;
for (char c : token) {
result = result * 10 + (c - '0');
}
return result;
}
// Count tokens in pointer
static consteval std::size_t count_tokens() {
if (pointer_str.empty() || pointer_str == "/") return 0;
std::size_t count = 0;
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
while (pos < pointer_str.size()) {
++count;
std::size_t next_slash = pointer_str.find('/', pos);
if (next_slash == std::string_view::npos) break;
pos = next_slash + 1;
}
return count;
}
// Get Nth token
static consteval std::string_view get_token(std::size_t token_index) {
std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
std::size_t current_token = 0;
while (current_token < token_index) {
std::size_t next_slash = pointer_str.find('/', pos);
pos = next_slash + 1;
++current_token;
}
std::size_t token_end = pointer_str.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_str.size();
return pointer_str.substr(pos, token_end - pos);
}
};
// JSON Pointer accessor
template<typename T, constevalutil::fixed_string Pointer>
struct pointer_accessor {
using parser = json_pointer_parser<Pointer>;
static constexpr std::string_view pointer_view = Pointer.view();
static constexpr std::size_t token_count = parser::count_tokens();
// Validate pointer against struct definition
static consteval bool validate_pointer() {
if constexpr (!std::is_class_v<T>) {
return true;
}
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
// Extract token up to next /
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
return false;
}
current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
} else {
bool found = false;
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
found = true;
break;
}
}
if (!found) return false;
}
pos = token_end + 1;
}
return true;
}
// Recursive accessor
template<std::size_t TokenIndex>
static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
if constexpr (TokenIndex >= token_count) {
return current;
} else {
constexpr std::string_view token = parser::get_token(TokenIndex);
if constexpr (parser::is_numeric(token)) {
constexpr std::size_t index = parser::parse_index(token);
auto arr = current.get_array().value_unsafe();
auto next_value = arr.at(index);
return access_impl<TokenIndex + 1>(next_value);
} else {
auto obj = current.get_object().value_unsafe();
auto next_value = obj.find_field_unordered(token);
return access_impl<TokenIndex + 1>(next_value);
}
}
}
// Access JSON value at pointer
template<typename DocOrValue>
static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
if constexpr (std::is_class_v<T>) {
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
}
if (pointer_view.empty() || pointer_view == "/") {
if constexpr (requires { doc_or_val.get_value(); }) {
return doc_or_val.get_value();
} else {
return doc_or_val;
}
}
simdjson_result<value> current = doc_or_val.get_value();
return access_impl<0>(current);
}
// Extract value at pointer directly into target with type validation
template<typename DocOrValue, typename FieldType>
static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
constexpr bool pointer_valid = validate_pointer();
static_assert(pointer_valid, "JSON Pointer does not match struct definition");
constexpr auto final_type = get_final_type();
static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
simdjson_result<value> current_value = doc_or_val.get_value();
auto json_value = access_impl<0>(current_value);
if (json_value.error()) return json_value.error();
return json_value.get(target);
}
private:
// Get final type by walking pointer through struct
template<typename U = T>
static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
auto current_type = ^^T;
std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
while (pos < pointer_view.size()) {
std::size_t token_end = pointer_view.find('/', pos);
if (token_end == std::string_view::npos) token_end = pointer_view.size();
std::string_view token = pointer_view.substr(pos, token_end - pos);
if (parser::is_numeric(token)) {
current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
} else {
auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
for (auto mem : members) {
if (std::meta::identifier_of(mem) == token) {
current_type = std::meta::type_of(mem);
break;
}
}
}
pos = token_end + 1;
}
return current_type;
}
};
// Compile-time JSON Pointer accessor with validation
template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::lasx::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<T, Pointer>;
return accessor::access(doc_or_val);
}
// Overload without type parameter (no validation)
template<constevalutil::fixed_string Pointer, typename DocOrValue>
inline simdjson_result<::simdjson::lasx::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
using accessor = pointer_accessor<void, Pointer>;
return accessor::access(doc_or_val);
}
} // namespace json_path
} // namespace ondemand
} // namespace lasx
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
/* end file simdjson/generic/ondemand/compile_time_accessors.h for lasx */
/* end file simdjson/generic/ondemand/amalgamated.h for lasx */
/* including simdjson/lasx/end.h: #include "simdjson/lasx/end.h" */
/* begin file simdjson/lasx/end.h */
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/lasx/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
/* undefining SIMDJSON_IMPLEMENTATION from "lasx" */
#undef SIMDJSON_IMPLEMENTATION
#if SIMDJSON_CAN_ALWAYS_RUN_LASX
// nothing needed.
#else
SIMDJSON_UNTARGET_REGION
#endif
/* end file simdjson/lasx/end.h */
#endif // SIMDJSON_LASX_ONDEMAND_H
/* end file simdjson/lasx/ondemand.h */
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv_vls)
/* including simdjson/rvv-vls/ondemand.h: #include "simdjson/rvv-vls/ondemand.h" */
/* begin file simdjson/rvv-vls/ondemand.h */
#ifndef SIMDJSON_RVV_VLS_ONDEMAND_H
#define SIMDJSON_RVV_VLS_ONDEMAND_H
/* including simdjson/rvv-vls/begin.h: #include "simdjson/rvv-vls/begin.h" */
/* begin file simdjson/rvv-vls/begin.h */
/* defining SIMDJSON_IMPLEMENTATION to "rvv_vls" */
#define SIMDJSON_IMPLEMENTATION rvv_vls
/* including simdjson/rvv-vls/base.h: #include "simdjson/rvv-vls/base.h" */
/* begin file simdjson/rvv-vls/base.h */
#ifndef SIMDJSON_RVV_VLS_BASE_H
#define SIMDJSON_RVV_VLS_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* RVV-VLS implementation.
*/
namespace rvv_vls {
class implementation;
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BASE_H
/* end file simdjson/rvv-vls/base.h */
/* including simdjson/rvv-vls/intrinsics.h: #include "simdjson/rvv-vls/intrinsics.h" */
/* begin file simdjson/rvv-vls/intrinsics.h */
#ifndef SIMDJSON_RVV_VLS_INTRINSICS_H
#define SIMDJSON_RVV_VLS_INTRINSICS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <riscv_vector.h>
#define simdutf_vrgather_u8m1x2(tbl, idx) \
__riscv_vcreate_v_u8m1_u8m2( \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 0), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 1), \
__riscv_vsetvlmax_e8m1()))
#define simdutf_vrgather_u8m1x4(tbl, idx) \
__riscv_vcreate_v_u8m1_u8m4( \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 0), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 1), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 2), \
__riscv_vsetvlmax_e8m1()), \
__riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 3), \
__riscv_vsetvlmax_e8m1()))
#if __riscv_zbc
#include <riscv_bitmanip.h>
#endif
#endif // SIMDJSON_RVV_VLS_INTRINSICS_H
/* end file simdjson/rvv-vls/intrinsics.h */
/* including simdjson/rvv-vls/bitmanipulation.h: #include "simdjson/rvv-vls/bitmanipulation.h" */
/* begin file simdjson/rvv-vls/bitmanipulation.h */
#ifndef SIMDJSON_RVV_VLS_BITMANIPULATION_H
#define SIMDJSON_RVV_VLS_BITMANIPULATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
// We sometimes call trailing_zero on inputs that are zero,
// but the algorithms do not end up using the returned value.
// Sadly, sanitizers are not smart enough to figure it out.
SIMDJSON_NO_SANITIZE_UNDEFINED
// This function can be used safely even if not all bytes have been
// initialized.
// See issue https://github.com/simdjson/simdjson/issues/1965
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_inline int trailing_zeroes(uint64_t input_num) {
return __builtin_ctzll(input_num);
}
/* result might be undefined when input_num is zero */
simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) {
return input_num & (input_num-1);
}
/* result might be undefined when input_num is zero */
simdjson_inline int leading_zeroes(uint64_t input_num) {
return __builtin_clzll(input_num);
}
simdjson_inline long long int count_ones(uint64_t input_num) {
return __builtin_popcountll(input_num);
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BITMANIPULATION_H
/* end file simdjson/rvv-vls/bitmanipulation.h */
/* including simdjson/rvv-vls/bitmask.h: #include "simdjson/rvv-vls/bitmask.h" */
/* begin file simdjson/rvv-vls/bitmask.h */
#ifndef SIMDJSON_RVV_VLS_BITMASK_H
#define SIMDJSON_RVV_VLS_BITMASK_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/intrinsics.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
//
// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered.
//
// For example, prefix_xor(00100100) == 00011100
//
simdjson_inline uint64_t prefix_xor(uint64_t bitmask) {
#if __riscv_zbc
return __riscv_clmul_64(bitmask, ~(uint64_t)0);
#elif __riscv_zvbc
return __riscv_vmv_x(__riscv_vclmul(__riscv_vmv_s_x_u64m1(bitmask, 1), ~(uint64_t)0, 1));
#else
bitmask ^= bitmask << 1;
bitmask ^= bitmask << 2;
bitmask ^= bitmask << 4;
bitmask ^= bitmask << 8;
bitmask ^= bitmask << 16;
bitmask ^= bitmask << 32;
#endif
return bitmask;
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_BITMASK_H
/* end file simdjson/rvv-vls/bitmask.h */
/* including simdjson/rvv-vls/simd.h: #include "simdjson/rvv-vls/simd.h" */
/* begin file simdjson/rvv-vls/simd.h */
#ifndef SIMDJSON_RVV_VLS_SIMD_H
#define SIMDJSON_RVV_VLS_SIMD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/bitmanipulation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/simdprune_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
namespace simd {
#if __riscv_v_fixed_vlen >= 512
static constexpr size_t VL8 = 512/8;
using vint8_t = vint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vuint8_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vbool_t = vbool8_t __attribute__((riscv_rvv_vector_bits(512/8)));
using vbitmask_t = uint64_t;
#else
static constexpr size_t VL8 = __riscv_v_fixed_vlen/8;
using vint8_t = vint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
using vuint8_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen)));
using vbool_t = vbool8_t __attribute__((riscv_rvv_vector_bits(__riscv_v_fixed_vlen/8)));
#if __riscv_v_fixed_vlen == 128
using vbitmask_t = uint16_t;
#elif __riscv_v_fixed_vlen == 256
using vbitmask_t = uint32_t;
#endif
#endif
#if __riscv_v_fixed_vlen == 128
using vuint8x64_t = vuint8m4_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool2_t __attribute__((riscv_rvv_vector_bits(512/8)));
#elif __riscv_v_fixed_vlen == 256
using vuint8x64_t = vuint8m2_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool4_t __attribute__((riscv_rvv_vector_bits(512/8)));
#else
using vuint8x64_t = vuint8m1_t __attribute__((riscv_rvv_vector_bits(512)));
using vboolx64_t = vbool8_t __attribute__((riscv_rvv_vector_bits(512/8)));
#endif
template<typename T>
struct simd8;
// SIMD byte mask type (returned by things like eq and gt)
template<>
struct simd8<bool> {
vbool_t value;
using bitmask_t = vbitmask_t;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vbool_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(__riscv_vmclr_m_b8(VL8)) {}
simdjson_inline simd8(bool _value) : simd8(splat(_value)) {}
simdjson_inline operator const vbool_t&() const { return value; }
simdjson_inline operator vbool_t&() { return value; }
static simdjson_inline simd8<bool> splat(bool _value) {
return __riscv_vreinterpret_b8(__riscv_vmv_v_x_u64m1(((uint64_t)!_value)-1, 1));
}
simdjson_inline vbitmask_t to_bitmask() const {
#if __riscv_v_fixed_vlen == 128
return __riscv_vmv_x(__riscv_vreinterpret_u16m1(value));
#elif __riscv_v_fixed_vlen == 256
return __riscv_vmv_x(__riscv_vreinterpret_u32m1(value));
#else
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(value));
#endif
}
// Bit operations
simdjson_inline simd8<bool> operator|(const simd8<bool> other) const { return __riscv_vmor(*this, other, VL8); }
simdjson_inline simd8<bool> operator&(const simd8<bool> other) const { return __riscv_vmand(*this, other, VL8); }
simdjson_inline simd8<bool> operator^(const simd8<bool> other) const { return __riscv_vmxor(*this, other, VL8); }
simdjson_inline simd8<bool> bit_andnot(const simd8<bool> other) const { return __riscv_vmandn(other, *this, VL8); }
simdjson_inline simd8<bool> operator~() const { return __riscv_vmnot(*this, VL8); }
simdjson_inline simd8<bool>& operator|=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast | other; return *this_cast; }
simdjson_inline simd8<bool>& operator&=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast & other; return *this_cast; }
simdjson_inline simd8<bool>& operator^=(const simd8<bool> other) { auto this_cast = static_cast<simd8<bool>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
};
// Unsigned bytes
template<>
struct simd8<uint8_t> {
vuint8_t value;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vuint8_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(zero()) {}
simdjson_inline simd8(const uint8_t values[VL8]) : simd8(load(values)) {}
simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
simdjson_inline simd8(simd8<bool> mask) : value(__riscv_vmerge_vxm_u8m1(zero(), -1, (vbool_t)mask, VL8)) {}
simdjson_inline operator const vuint8_t&() const { return this->value; }
simdjson_inline operator vuint8_t&() { return this->value; }
simdjson_inline simd8(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) : simd8(vuint8_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<uint8_t> repeat_16(
uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
) {
return simd8<uint8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
static simdjson_inline vuint8_t splat(uint8_t _value) { return __riscv_vmv_v_x_u8m1(_value, VL8); }
static simdjson_inline vuint8_t zero() { return splat(0); }
static simdjson_inline vuint8_t load(const uint8_t values[VL8]) { return __riscv_vle8_v_u8m1(values, VL8); }
// Bit operations
simdjson_inline simd8<uint8_t> operator|(const simd8<uint8_t> other) const { return __riscv_vor_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator&(const simd8<uint8_t> other) const { return __riscv_vand_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator^(const simd8<uint8_t> other) const { return __riscv_vxor_vv_u8m1( value, other, VL8); }
simdjson_inline simd8<uint8_t> operator~() const { return __riscv_vnot_v_u8m1(value, VL8); }
#if __riscv_zvbb
simdjson_inline simd8<uint8_t> bit_andnot(const simd8<uint8_t> other) const { return __riscv_vandn_vv_u8m1(other, value, VL8); }
#else
simdjson_inline simd8<uint8_t> bit_andnot(const simd8<uint8_t> other) const { return other & ~*this; }
#endif
simdjson_inline simd8<uint8_t>& operator|=(const simd8<uint8_t> other) { value = *this | other; return *this; }
simdjson_inline simd8<uint8_t>& operator&=(const simd8<uint8_t> other) { value = *this & other; return *this; }
simdjson_inline simd8<uint8_t>& operator^=(const simd8<uint8_t> other) { value = *this ^ other; return *this; }
simdjson_inline simd8<bool> operator==(const simd8<uint8_t> other) const { return __riscv_vmseq(value, other, VL8); }
simdjson_inline simd8<bool> operator==(uint8_t other) const { return __riscv_vmseq(value, other, VL8); }
template<int N=1>
simdjson_inline simd8<uint8_t> prev(const simd8<uint8_t> prev_chunk) const {
return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8);
}
// Store to array
simdjson_inline void store(uint8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); }
// Saturated math
simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return __riscv_vsaddu(value, other, VL8); }
simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return __riscv_vssubu(value, other, VL8); }
// Addition/subtraction are the same for signed and unsigned
simdjson_inline simd8<uint8_t> operator+(const simd8<uint8_t> other) const { return __riscv_vadd(value, other, VL8); }
simdjson_inline simd8<uint8_t> operator-(const simd8<uint8_t> other) const { return __riscv_vsub(value, other, VL8); }
simdjson_inline simd8<uint8_t>& operator+=(const simd8<uint8_t> other) { value = *this + other; return *this; }
simdjson_inline simd8<uint8_t>& operator-=(const simd8<uint8_t> other) { value = *this - other; return *this; }
// Order-specific operations
simdjson_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return __riscv_vmsleu(value, other, VL8); }
simdjson_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return __riscv_vmsgeu(value, other, VL8); }
simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return __riscv_vmsltu(value, other, VL8); }
simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return __riscv_vmsgtu(value, other, VL8); }
// Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero.
simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this > other); }
// Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero.
simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this < other); }
// Bit-specific operations
simdjson_inline bool any_bits_set_anywhere() const {
return __riscv_vfirst(__riscv_vmsne(value, 0, VL8), VL8) >= 0;
}
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return __riscv_vsrl(value, N, VL8); }
template<int N>
simdjson_inline simd8<uint8_t> shl() const { return __riscv_vsll(value, N, VL8); }
// Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __riscv_vrgather(lookup_table, value, VL8);
}
// compress inactive elements, to match AVX-512 behavior
template<typename L>
simdjson_inline void compress(vbitmask_t mask, L * output) const {
mask = (vbitmask_t)~mask;
#if __riscv_v_fixed_vlen == 128
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u16m1(mask, 1));
#elif __riscv_v_fixed_vlen == 256
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u32m1(mask, 1));
#else
vbool8_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u64m1(mask, 1));
#endif
__riscv_vse8_v_u8m1(output, __riscv_vcompress(value, m, VL8), count_ones(mask));
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
// Signed bytes
template<>
struct simd8<int8_t> {
vint8_t value;
static constexpr int SIZE = sizeof(value);
simdjson_inline simd8(const vint8_t _value) : value(_value) {}
simdjson_inline simd8() : simd8(zero()) {}
simdjson_inline simd8(const int8_t values[VL8]) : simd8(load(values)) {}
simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
simdjson_inline operator const vint8_t&() const { return this->value; }
simdjson_inline operator vint8_t&() { return this->value; }
simdjson_inline simd8(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) : simd8(vint8_t{
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
}) {}
// Repeat 16 values as many times as necessary (usually for lookup tables)
simdjson_inline static simd8<int8_t> repeat_16(
int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
) {
return simd8<int8_t>(
v0, v1, v2, v3, v4, v5, v6, v7,
v8, v9, v10,v11,v12,v13,v14,v15
);
}
static simdjson_inline vint8_t splat(int8_t _value) { return __riscv_vmv_v_x_i8m1(_value, VL8); }
static simdjson_inline vint8_t zero() { return splat(0); }
static simdjson_inline vint8_t load(const int8_t values[VL8]) { return __riscv_vle8_v_i8m1(values, VL8); }
simdjson_inline void store(int8_t dst[VL8]) const { return __riscv_vse8(dst, value, VL8); }
// Explicit conversion to/from unsigned
simdjson_inline explicit simd8(const vuint8_t other): simd8(__riscv_vreinterpret_i8m1(other)) {}
simdjson_inline explicit operator simd8<uint8_t>() const { return __riscv_vreinterpret_u8m1(value); }
// Math
simdjson_inline simd8<int8_t> operator+(const simd8<int8_t> other) const { return __riscv_vadd(value, other, VL8); }
simdjson_inline simd8<int8_t> operator-(const simd8<int8_t> other) const { return __riscv_vsub(value, other, VL8); }
simdjson_inline simd8<int8_t>& operator+=(const simd8<int8_t> other) { value = *this + other; return *this; }
simdjson_inline simd8<int8_t>& operator-=(const simd8<int8_t> other) { value = *this - other; return *this; }
// Order-sensitive comparisons
simdjson_inline simd8<int8_t> max_val( const simd8<int8_t> other) const { return __riscv_vmax( value, other, VL8); }
simdjson_inline simd8<int8_t> min_val( const simd8<int8_t> other) const { return __riscv_vmin( value, other, VL8); }
simdjson_inline simd8<bool> operator>( const simd8<int8_t> other) const { return __riscv_vmsgt(value, other, VL8); }
simdjson_inline simd8<bool> operator<( const simd8<int8_t> other) const { return __riscv_vmslt(value, other, VL8); }
simdjson_inline simd8<bool> operator==(const simd8<int8_t> other) const { return __riscv_vmseq(value, other, VL8); }
template<int N=1>
simdjson_inline simd8<int8_t> prev(const simd8<int8_t> prev_chunk) const {
return __riscv_vslideup(__riscv_vslidedown(prev_chunk, VL8-N, VL8), value, N, VL8);
}
// Perform a lookup assuming no value is larger than 16
template<typename L>
simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
return __riscv_vrgather(lookup_table, value, VL8);
}
template<typename L>
simdjson_inline simd8<L> lookup_16(
L replace0, L replace1, L replace2, L replace3,
L replace4, L replace5, L replace6, L replace7,
L replace8, L replace9, L replace10, L replace11,
L replace12, L replace13, L replace14, L replace15) const {
return lookup_16(simd8<L>::repeat_16(
replace0, replace1, replace2, replace3,
replace4, replace5, replace6, replace7,
replace8, replace9, replace10, replace11,
replace12, replace13, replace14, replace15
));
}
};
template<typename T>
struct simd8x64;
template<>
struct simd8x64<uint8_t> {
static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<uint8_t>);
vuint8x64_t value;
#if __riscv_v_fixed_vlen >= 512
template<int idx> simd8<uint8_t> get() const { return value; }
#else
template<int idx> simd8<uint8_t> get() const { return __riscv_vget_u8m1(value, idx); }
#endif
simdjson_inline operator const vuint8x64_t&() const { return this->value; }
simdjson_inline operator vuint8x64_t&() { return this->value; }
simd8x64(const simd8x64<uint8_t>& o) = delete; // no copy allowed
simd8x64<uint8_t>& operator=(const simd8<uint8_t>& other) = delete; // no assignment allowed
simd8x64() = delete; // no default constructor allowed
#if __riscv_v_fixed_vlen == 128
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m4(ptr, n)) {}
#elif __riscv_v_fixed_vlen == 256
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m2(ptr, n)) {}
#else
simdjson_inline simd8x64(const uint8_t *ptr, size_t n = 64) : value(__riscv_vle8_v_u8m1(ptr, n)) {}
#endif
simdjson_inline void store(uint8_t ptr[64]) const {
__riscv_vse8(ptr, value, 64);
}
simdjson_inline bool is_ascii() const {
#if __riscv_v_fixed_vlen == 128
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m4(value), 0, 64), 64) < 0;
#elif __riscv_v_fixed_vlen == 256
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m2(value), 0, 64), 64) < 0;
#else
return __riscv_vfirst(__riscv_vmslt(__riscv_vreinterpret_i8m1(value), 0, 64), 64) < 0;
#endif
}
// compress inactive elements, to match AVX-512 behavior
simdjson_inline uint64_t compress(uint64_t mask, uint8_t * output) const {
mask = ~mask;
#if __riscv_v_fixed_vlen == 128
vboolx64_t m = __riscv_vreinterpret_b2(__riscv_vmv_s_x_u64m1(mask, 1));
#elif __riscv_v_fixed_vlen == 256
vboolx64_t m = __riscv_vreinterpret_b4(__riscv_vmv_s_x_u64m1(mask, 1));
#else
vboolx64_t m = __riscv_vreinterpret_b8(__riscv_vmv_s_x_u64m1(mask, 1));
#endif
size_t cnt = count_ones(mask);
__riscv_vse8(output, __riscv_vcompress(value, m, 64), cnt);
return cnt;
}
simdjson_inline uint64_t eq(const uint8_t m) const {
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vmseq(value, m, 64)));
}
simdjson_inline uint64_t lteq(const uint8_t m) const {
return __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vmsleu(value, m, 64)));
}
}; // struct simd8x64<uint8_t>
} // namespace simd
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_SIMD_H
/* end file simdjson/rvv-vls/simd.h */
/* including simdjson/rvv-vls/stringparsing_defs.h: #include "simdjson/rvv-vls/stringparsing_defs.h" */
/* begin file simdjson/rvv-vls/stringparsing_defs.h */
#ifndef SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
#define SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace {
using namespace simd;
// Holds backslashes and quotes locations.
struct backslash_and_quote {
public:
static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
simdjson_inline backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; }
simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; }
simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); }
simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); }
uint64_t bs_bits;
uint64_t quote_bits;
}; // struct backslash_and_quote
simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return { (v == '\\').to_bitmask(), (v == '"').to_bitmask() };
}
struct escaping {
static constexpr uint64_t BYTES_PROCESSED = sizeof(simd8<uint8_t>);
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
return { ((v == '"') | (v == '\\') | (v == 32)).to_bitmask() };
}
} // unnamed namespace
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_RVV_VLS_STRINGPARSING_DEFS_H
/* end file simdjson/rvv-vls/stringparsing_defs.h */
/* including simdjson/rvv-vls/numberparsing_defs.h: #include "simdjson/rvv-vls/numberparsing_defs.h" */
/* begin file simdjson/rvv-vls/numberparsing_defs.h */
#ifndef SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
#define SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/rvv-vls/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/numberparsing_tables.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <cstring>
#ifdef JSON_TEST_NUMBERS // for unit testing
void found_invalid_number(const uint8_t *buf);
void found_integer(int64_t result, const uint8_t *buf);
void found_unsigned_integer(uint64_t result, const uint8_t *buf);
void found_float(double result, const uint8_t *buf);
#endif
namespace simdjson {
namespace rvv_vls {
namespace numberparsing {
// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) {
uint64_t val;
#if __riscv_misaligned_fast
memcpy(&val, chars, sizeof(uint64_t));
#else
val = __riscv_vmv_x(__riscv_vreinterpret_u64m1(__riscv_vlmul_ext_u8m1(__riscv_vle8_v_u8mf2((uint8_t*)chars, 8))));
#endif
val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8;
val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16;
return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32);
}
/** @private */
static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) {
return parse_eight_digits_unrolled(reinterpret_cast<const char *>(chars));
}
/** @private */
simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t value2) {
internal::value128 answer;
__uint128_t r = (static_cast<__uint128_t>(value1)) * value2;
answer.low = uint64_t(r);
answer.high = uint64_t(r >> 64);
return answer;
}
} // namespace numberparsing
} // namespace rvv_vls
} // namespace simdjson
#define SIMDJSON_SWAR_NUMBER_PARSING 1
#endif // SIMDJSON_RVV_VLS_NUMBERPARSING_DEFS_H
/* end file simdjson/rvv-vls/numberparsing_defs.h */
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
/* end file simdjson/rvv-vls/begin.h */
/* including simdjson/generic/ondemand/amalgamated.h for rvv_vls: #include "simdjson/generic/ondemand/amalgamated.h" */
/* begin file simdjson/generic/ondemand/amalgamated.h for rvv_vls */
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
// Stuff other things depend on
/* including simdjson/generic/ondemand/base.h for rvv_vls: #include "simdjson/generic/ondemand/base.h" */
/* begin file simdjson/generic/ondemand/base.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_BASE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
/**
* A fast, simple, DOM-like interface that parses JSON as you use it.
*
* Designed for maximum speed and a lower memory profile.
*/
namespace ondemand {
/** Represents the depth of a JSON value (number of nested arrays/objects). */
using depth_t = int32_t;
/** @copydoc simdjson::rvv_vls::number_type */
using number_type = simdjson::rvv_vls::number_type;
/** @private Position in the JSON buffer indexes */
using token_position = const uint32_t *;
class array;
class array_iterator;
class document;
class document_reference;
class document_stream;
class field;
class json_iterator;
enum class json_type;
struct number;
class object;
class object_iterator;
class parser;
class raw_json_string;
class token_iterator;
class value;
class value_iterator;
#if SIMDJSON_SUPPORTS_RANGES
class array_range;
class array_range_iterator;
class object_range;
class object_range_iterator;
#endif // SIMDJSON_SUPPORTS_RANGES
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H
/* end file simdjson/generic/ondemand/base.h for rvv_vls */
/* including simdjson/generic/ondemand/deserialize.h for rvv_vls: #include "simdjson/generic/ondemand/deserialize.h" */
/* begin file simdjson/generic/ondemand/deserialize.h for rvv_vls */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
struct deserialize_tag;
/// These types are deserializable in a built-in way
template <typename> struct is_builtin_deserializable : std::false_type {};
template <> struct is_builtin_deserializable<int64_t> : std::true_type {};
template <> struct is_builtin_deserializable<uint64_t> : std::true_type {};
template <> struct is_builtin_deserializable<double> : std::true_type {};
template <> struct is_builtin_deserializable<bool> : std::true_type {};
template <> struct is_builtin_deserializable<rvv_vls::ondemand::array> : std::true_type {};
template <> struct is_builtin_deserializable<rvv_vls::ondemand::object> : std::true_type {};
template <> struct is_builtin_deserializable<rvv_vls::ondemand::value> : std::true_type {};
template <> struct is_builtin_deserializable<rvv_vls::ondemand::raw_json_string> : std::true_type {};
template <> struct is_builtin_deserializable<std::string_view> : std::true_type {};
#if SIMDJSON_SUPPORTS_CHAR8_T
template <> struct is_builtin_deserializable<std::u8string_view> : std::true_type {};
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename T>
concept is_builtin_deserializable_v = is_builtin_deserializable<T>::value;
template <typename T, typename ValT = rvv_vls::ondemand::value>
concept custom_deserializable = tag_invocable<deserialize_tag, ValT&, T&>;
template <typename T, typename ValT = rvv_vls::ondemand::value>
concept deserializable = custom_deserializable<T, ValT> || is_builtin_deserializable_v<T> || concepts::optional_type<T>;
template <typename T, typename ValT = rvv_vls::ondemand::value>
concept nothrow_custom_deserializable = nothrow_tag_invocable<deserialize_tag, ValT&, T&>;
// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned.
template <typename T, typename ValT = rvv_vls::ondemand::value>
concept nothrow_deserializable = nothrow_custom_deserializable<T, ValT> || is_builtin_deserializable_v<T>;
// True iff get<T>() on ValT cannot throw: no custom tag_invoke, or that tag_invoke is noexcept.
template <typename T, typename ValT = rvv_vls::ondemand::value>
concept nothrow_gettable = !custom_deserializable<T, ValT> || nothrow_custom_deserializable<T, ValT>;
/// Deserialize Tag
inline constexpr struct deserialize_tag {
using array_type = rvv_vls::ondemand::array;
using object_type = rvv_vls::ondemand::object;
using value_type = rvv_vls::ondemand::value;
using document_type = rvv_vls::ondemand::document;
using document_reference_type = rvv_vls::ondemand::document_reference;
// Customization Point for array
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(array_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for object
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(object_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for value
template <typename T>
requires custom_deserializable<T, value_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, value_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document
template <typename T>
requires custom_deserializable<T, document_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_type>) {
return tag_invoke(*this, object, output);
}
// Customization Point for document reference
template <typename T>
requires custom_deserializable<T, document_reference_type>
simdjson_warn_unused constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable<T, document_reference_type>) {
return tag_invoke(*this, object, output);
}
} deserialize{};
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/deserialize.h for rvv_vls */
/* including simdjson/generic/ondemand/value_iterator.h for rvv_vls: #include "simdjson/generic/ondemand/value_iterator.h" */
/* begin file simdjson/generic/ondemand/value_iterator.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* Iterates through a single JSON value at a particular depth.
*
* Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
* the caller to call the right ones.
*
* @private This is not intended for external use.
*/
class value_iterator {
protected:
/** The underlying JSON iterator */
json_iterator *_json_iter{};
/** The depth of this value */
depth_t _depth{};
/**
* The starting token index for this value
*/
token_position _start_position{};
public:
simdjson_inline value_iterator() noexcept = default;
/**
* Denote that we're starting a document.
*/
simdjson_inline void start_document() noexcept;
/**
* Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
*
* Optimized for scalars.
*/
simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is at the start of the value
*/
simdjson_inline bool at_start() const noexcept;
/**
* Tell whether the value is open--if the value has not been used, or the array/object is still open.
*/
simdjson_inline bool is_open() const noexcept;
/**
* Tell whether the value is at an object's first field (just after the {).
*/
simdjson_inline bool at_first_field() const noexcept;
/**
* Abandon all iteration.
*/
simdjson_inline void abandon() noexcept;
/**
* Get the child value as a value_iterator.
*/
simdjson_inline value_iterator child_value() const noexcept;
/**
* Get the depth of this value.
*/
simdjson_inline int32_t depth() const noexcept;
/**
* Get the JSON type of this value.
*
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() const noexcept;
/**
* @addtogroup object Object iteration
*
* Methods to iterate and find object fields. These methods generally *assume* the value is
* actually an object; the caller is responsible for keeping track of that fact.
*
* @{
*/
/**
* Start an object iteration.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
/**
* Start an object iteration from the root.
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
/**
* Checks whether an object could be started from the root. May be called by start_root_object.
*
* @returns SUCCESS if it is possible to safely start an object from the root (document level).
* @error INCORRECT_TYPE if there is no opening {
* @error TAPE_ERROR if there is no matching } at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept;
/**
* Start an object iteration after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
/**
* Start an object iteration from the root, after the user has already checked and moved past the {.
*
* Does not move the iterator unless the object is empty ({}).
*
* @returns Whether the object had any fields (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
/**
* Moves to the next field in an object.
*
* Looks for , and }. If } is found, the object is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return whether there is another field in the object.
* @error TAPE_ERROR If there is a comma missing between fields.
* @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
/**
* Get the current field's key.
*/
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
/**
* Get the current field's key together with its raw byte length.
*
* Like field_key(), but also returns the number of raw key bytes (the distance
* from the first key byte to the closing quote). The length is recovered from
* the structural index -- the next structural token is the ':' -- by stepping
* back over any whitespace to the closing quote, avoiding a forward SIMD scan
* for the closing quote. Leaves the iterator positioned exactly as field_key().
*/
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept;
/**
* Pass the : in the field and move to its value.
*/
simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
/**
* Find the next field with the given key.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
/**
* Find the next field with the given key, *without* unescaping. This assumes object order: it
* will not find the field if it was already passed when looking for some *other* field.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
/**
* Find the field with the given key without regard to order, and *without* unescaping.
*
* This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
*
* Assumes you have called next_field() or otherwise matched the previous value.
*
* This means the iterator must be sitting at the next key:
*
* ```
* { "a": 1, "b": 2 }
* ^
* ```
*
* Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
* unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
* fail to match some keys with escapes (\u, \n, etc.).
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
/** @} */
/**
* @addtogroup array Array iteration
* Methods to iterate over array elements. These methods generally *assume* the value is actually
* an object; the caller is responsible for keeping track of that fact.
* @{
*/
/**
* Check for an opening [ and start an array iteration.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
/**
* Check for an opening [ and start an array iteration while at the root.
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
/**
* Checks whether an array could be started from the root. May be called by start_root_array.
*
* @returns SUCCESS if it is possible to safely start an array from the root (document level).
* @error INCORRECT_TYPE If there is no [.
* @error TAPE_ERROR if there is no matching ] at end of document
*/
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept;
/**
* Start an array iteration, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
/**
* Start an array iteration from the root, after the user has already checked and moved past the [.
*
* Does not move the iterator unless the array is empty ([]).
*
* @returns Whether the array had any elements (returns false for empty).
* @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
* array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that
* invalidates the document.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
/**
* Moves to the next element in an array.
*
* Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
* Otherwise, it advances to the next value.
*
* @return Whether there is another element in the array.
* @error TAPE_ERROR If there is a comma missing between elements.
*/
simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
/**
* Get a child value iterator.
*/
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
/** @} */
/**
* @defgroup scalar Scalar values
* @addtogroup scalar
* @{
*/
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<float> get_root_float_in_string(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
simdjson_warn_unused simdjson_inline error_code error() const noexcept;
simdjson_inline uint8_t *&string_buf_loc() noexcept;
simdjson_inline const json_iterator &json_iter() const noexcept;
simdjson_inline json_iterator &json_iter() noexcept;
simdjson_inline void assert_is_valid() const noexcept;
simdjson_inline bool is_valid() const noexcept;
/** @} */
protected:
/**
* Restarts an array iteration.
* @returns Whether the array has any elements (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_array() noexcept;
/**
* Restarts an object iteration.
* @returns Whether the object has any fields (returns false for empty).
*/
simdjson_inline simdjson_result<bool> reset_object() noexcept;
/**
* move_at_start(): moves us so that we are pointing at the beginning of
* the container. It updates the index so that at_start() is true and it
* syncs the depth. The user can then create a new container instance.
*
* Usage: used with value::count_elements().
**/
simdjson_inline void move_at_start() noexcept;
/**
* move_at_container_start(): moves us so that we are pointing at the beginning of
* the container so that assert_at_container_start() passes.
*
* Usage: used with reset_array() and reset_object().
**/
simdjson_inline void move_at_container_start() noexcept;
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
simdjson_inline const uint8_t *peek_start() const noexcept;
simdjson_inline uint32_t peek_start_length() const noexcept;
simdjson_inline uint32_t peek_root_length() const noexcept;
/**
* The general idea of the advance_... methods and the peek_* methods
* is that you first peek and check that you have desired type. If you do,
* and only if you do, then you advance.
*
* We used to unconditionally advance. But this made reasoning about our
* current state difficult.
* Suppose you always advance. Look at the 'value' matching the key
* "shadowable" in the following example...
*
* ({"globals":{"a":{"shadowable":[}}}})
*
* If the user thinks it is a Boolean and asks for it, then we check the '[',
* decide it is not a Boolean, but still move into the next character ('}'). Now
* we are left pointing at '}' right after a '['. And we have not yet reported
* an error, only that we do not have a Boolean.
*
* If, instead, you just stand your ground until it is content that you know, then
* you will only even move beyond the '[' if the user tells you that you have an
* array. So you will be at the '}' character inside the array and, hopefully, you
* will then catch the error because an array cannot start with '}', but the code
* processing Boolean values does not know this.
*
* So the contract is: first call 'peek_...' and then call 'advance_...' only
* if you have determined that it is a type you can handle.
*
* Unfortunately, it makes the code more verbose, longer and maybe more error prone.
*/
simdjson_inline void advance_scalar(const char *type) noexcept;
simdjson_inline void advance_root_scalar(const char *type) noexcept;
simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
simdjson_warn_unused simdjson_inline error_code end_container() noexcept;
/**
* Advance to a place expecting a value (increasing depth).
*
* @return The current token (the one left behind).
* @error TAPE_ERROR If the document ended early.
*/
simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
simdjson_inline bool is_at_start() const noexcept;
/**
* is_at_iterator_start() returns true on an array or object after it has just been
* created, whether the instance is empty or not.
*
* Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
*/
simdjson_inline bool is_at_iterator_start() const noexcept;
/**
* Assuming that we are within an object, this returns true if we
* are pointing at a key.
*
* Usage: the skip_child() method should never be used while we are pointing
* at a key inside an object.
*/
simdjson_inline bool is_at_key() const noexcept;
inline void assert_at_start() const noexcept;
inline void assert_at_container_start() const noexcept;
inline void assert_at_root() const noexcept;
inline void assert_at_child() const noexcept;
inline void assert_at_next() const noexcept;
inline void assert_at_non_root_start() const noexcept;
/** Get the starting position of this value */
simdjson_inline token_position start_position() const noexcept;
/** @copydoc error_code json_iterator::position() const noexcept; */
simdjson_inline token_position position() const noexcept;
/**
* Move the live iterator directly to the given position and depth, without
* validating against the parser's per-depth container-start bookkeeping
* (unlike json_iterator::reenter_child()). Used to restore a previously
* captured mid-container position (see object::revert_position()): that
* bookkeeping only tracks each container's own start, not every position
* a caller might later capture and revert to, so it does not apply here.
*/
simdjson_inline void reenter_at(token_position position, depth_t depth) noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position last_position() const noexcept;
/** @copydoc error_code json_iterator::end_position() const noexcept; */
simdjson_inline token_position end_position() const noexcept;
/** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
friend class document;
friend class object;
friend class object_iterator;
friend class array;
friend class value;
friend class field;
}; // value_iterator
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::value_iterator> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::value_iterator> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::value_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H
/* end file simdjson/generic/ondemand/value_iterator.h for rvv_vls */
/* including simdjson/generic/ondemand/value.h for rvv_vls: #include "simdjson/generic/ondemand/value.h" */
/* begin file simdjson/generic/ondemand/value.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <type_traits>
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* An ephemeral JSON value returned during iteration. It is only valid for as long as you do
* not access more data in the JSON document.
*/
class value {
public:
/**
* Create a new invalid value.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline value() noexcept = default;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
* When SIMDJSON_SUPPORTS_CONCEPTS is set, custom types are also supported.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, u8string_view (C++20), uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
* If the macro SIMDJSON_SUPPORTS_CONCEPTS is set, then custom types are also supported.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, value>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, value>) {
return deserialize(*this, out);
} else if constexpr (concepts::optional_type<T>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
return get<value_type>(out.value());
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif
}
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to a unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* In some instances, you may want to allow replacement of invalid Unicode sequences.
* You may do so by passing the allow_replacement parameter as true. In the following
* example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true
* to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode
* replacement character (U+FFFD).
*
* simdjson::ondemand::parser parser;
* auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded;
* simdjson::ondemand::document doc = parser.iterate(json);
* auto view = doc["deviceId"].get_string(true);
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: a value should be consumed once. Calling get_u8string() twice on the same
* value is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a "wobbly" string.
*
* The string is may not be a valid UTF-8 string.
* See https://simonsapin.github.io/wtf-8/
*
* Important: a value should be consumed once. Calling get_wobbly_string() twice on the same value
* is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.).
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::string_view>().
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator std::string_view() noexcept(false);
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
simdjson_inline operator raw_json_string() noexcept(false);
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
simdjson_inline operator bool() noexcept(false);
#endif
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* Performance hint: You should only call count_elements() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method on the object instance.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) noexcept;
simdjson_result<value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* @return The type of JSON value (json_type::array, json_type::object, json_type::string,
* json_type::number, json_type::boolean, or json_type::null).
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the value is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the value is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the value is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* Performance note: if you call this function systematically
* before parsing a number, you may have fallen for a performance
* anti-pattern.
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808.
* get_number_type() is number_type::big_integer for integers that do not fit in 64 bits,
* in which case the digit_count is set to the length of the big integer string.
* Otherwise, get_number_type() has value number_type::floating_point_number.
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* For integers that do not fit in 64 bits, the function returns BIGINT_ERROR error code.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*
* Performance note: this is designed with performance in mind. When
* calling 'get_number()', you scan the number string only once, determining
* efficiently the type and storing it in an efficient manner.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. However, if this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view is guaranteed to be
* a non-space token.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*
* See also value::raw_json().
*/
simdjson_inline std::string_view raw_json_token() noexcept;
/**
* Get a string_view pointing at this value in the JSON document.
* If this element is an array or an object, it consumes the array or the object
* and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
* If this element is a scalar (string, number, Boolean, null), it returns what
* raw_json_token() would return.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed.
*
* Calling at_pointer() on non-document instances (e.g., arrays and objects) is not
* standardized (by RFC 6901). We provide some experimental support for JSON pointers
* on non-document instances. Yet it is not the case when calling at_pointer on an array
* or an object instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
simdjson_inline simdjson_result<value> 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<value> at_path(std::string_view at_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard character (*) for arrays or ".*" for objects.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
protected:
/**
* Create a value.
*/
simdjson_inline value(const value_iterator &iter) noexcept;
/**
* Skip this value, allowing iteration to continue.
*/
simdjson_inline void skip() noexcept;
/**
* Start a value at the current position.
*
* (It should already be started; this is just a self-documentation method.)
*/
static simdjson_inline value start(const value_iterator &iter) noexcept;
/**
* Resume a value.
*/
static simdjson_inline value resume(const value_iterator &iter) noexcept;
/**
* Get the object, starting or resuming it as necessary
*/
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
// simdjson_inline void log_value(const char *type) const noexcept;
// simdjson_inline void log_error(const char *message) const noexcept;
value_iterator iter{};
friend class document;
friend class array_iterator;
friend class field;
friend class object;
friend struct simdjson_result<value>;
friend struct simdjson_result<field>;
friend class field;
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::value> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::value> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::value &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<rvv_vls::ondemand::array> get_array() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::object> get_object() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::value>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::value>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator rvv_vls::ondemand::array() noexcept(false);
simdjson_inline operator rvv_vls::ondemand::object() noexcept(false);
simdjson_inline operator uint64_t() noexcept(false);
simdjson_inline operator int64_t() noexcept(false);
simdjson_inline operator double() noexcept(false);
simdjson_inline operator std::string_view() noexcept(false);
simdjson_inline operator rvv_vls::ondemand::raw_json_string() noexcept(false);
simdjson_inline operator bool() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(std::string_view key) noexcept; */
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(const char *key) noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field as not there when they are not in order).
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(const char *key) noexcept;
/** @overload simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator[](std::string_view key) noexcept;
/** @overload simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(std::string_view key) noexcept; */
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator[](const char *key) noexcept;
simdjson_result<rvv_vls::ondemand::value> operator[](int) noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* Given a valid JSON document, the answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*/
simdjson_inline simdjson_result<rvv_vls::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<rvv_vls::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/** @copydoc simdjson_inline simdjson_result<const char *> current_location() noexcept */
simdjson_inline simdjson_result<const char *> current_location() noexcept;
/** @copydoc simdjson_inline int32_t current_depth() const noexcept */
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
};
// Forward-declare explicit specializations so MSVC /permissive- sees them before
// any template instantiation that would resolve element.get(val) to the primary.
template<> simdjson_inline error_code
simdjson_result<rvv_vls::ondemand::value>::get<rvv_vls::ondemand::value>(
rvv_vls::ondemand::value &out) noexcept;
template<> simdjson_inline simdjson_result<rvv_vls::ondemand::value>
simdjson_result<rvv_vls::ondemand::value>::get<rvv_vls::ondemand::value>() noexcept;
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_H
/* end file simdjson/generic/ondemand/value.h for rvv_vls */
/* including simdjson/generic/ondemand/logger.h for rvv_vls: #include "simdjson/generic/ondemand/logger.h" */
/* begin file simdjson/generic/ondemand/logger.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
// Logging should be free unless SIMDJSON_VERBOSE_LOGGING is set. Importantly, it is critical
// that the call to the log functions be side-effect free. Thus, for example, you should not
// create temporary std::string instances.
namespace logger {
enum class log_level : int32_t {
info = 0,
error = 1
};
#if SIMDJSON_VERBOSE_LOGGING
static constexpr const bool LOG_ENABLED = true;
#else
static constexpr const bool LOG_ENABLED = false;
#endif
// We do not want these functions to be 'really inlined' since real inlining is
// for performance purposes and if you are using the loggers, you do not care about
// performance (or should not).
static inline void log_headers() noexcept;
// If args are provided, title will be treated as format string
template <typename... Args>
static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
template <typename... Args>
static inline void 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;
static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept;
static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept;
static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept;
static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept;
static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept;
static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept;
} // namespace logger
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_H
/* end file simdjson/generic/ondemand/logger.h for rvv_vls */
/* including simdjson/generic/ondemand/token_iterator.h for rvv_vls: #include "simdjson/generic/ondemand/token_iterator.h" */
/* begin file simdjson/generic/ondemand/token_iterator.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `"<string>"` `123` `true` `false` `null`)
* detected by stage 1.
*
* @private This is not intended for external use.
*/
class token_iterator {
public:
/**
* Create a new invalid token_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline token_iterator() noexcept = default;
simdjson_inline token_iterator(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default;
simdjson_inline token_iterator(const token_iterator &other) noexcept = default;
simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default;
/**
* Advance to the next token (returning the current one).
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Reports the current offset in bytes from the start of the underlying buffer.
*/
simdjson_inline uint32_t current_offset() const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*
* TODO consider a string_view, assuming the length will get stripped out by the optimizer when
* it is not used...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a given token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for a root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token (start of the document).
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Return the current index.
*/
simdjson_inline token_position position() const noexcept;
/**
* Reset to a previously saved index.
*/
simdjson_inline void set_position(token_position target_position) noexcept;
// NOTE: we don't support a full C++ iterator interface, because we expect people to make
// different calls to advance the iterator based on *their own* state.
simdjson_inline bool operator==(const token_iterator &other) const noexcept;
simdjson_inline bool operator!=(const token_iterator &other) const noexcept;
simdjson_inline bool operator>(const token_iterator &other) const noexcept;
simdjson_inline bool operator>=(const token_iterator &other) const noexcept;
simdjson_inline bool operator<(const token_iterator &other) const noexcept;
simdjson_inline bool operator<=(const token_iterator &other) const noexcept;
protected:
simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept;
/**
* Get the index of the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = current token,
* 1 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept;
/**
* Get the index of the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @param position The position of the token.
*
*/
simdjson_inline uint32_t peek_index(token_position position) const noexcept;
const uint8_t *buf{};
token_position _position{};
friend class json_iterator;
friend class document_stream;
friend class value_iterator;
friend class object;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::token_iterator> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::token_iterator> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::token_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_H
/* end file simdjson/generic/ondemand/token_iterator.h for rvv_vls */
/* including simdjson/generic/ondemand/json_iterator.h for rvv_vls: #include "simdjson/generic/ondemand/json_iterator.h" */
/* begin file simdjson/generic/ondemand/json_iterator.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* Iterates through JSON tokens, keeping track of depth and string buffer.
*
* @private This is not intended for external use.
*/
class json_iterator {
protected:
token_iterator token{};
ondemand::parser *parser{};
/**
* Next free location in the string buffer.
*
* Used by raw_json_string::unescape() to have a place to unescape strings to.
*/
uint8_t *_string_buf_loc{};
/**
* JSON error, if there is one.
*
* INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever.
*
* PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first
* iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If
* this is not elided, we should make sure it's at least not using up a register. Failing that,
* we should store it in document so there's only one of them.
*/
error_code error{SUCCESS};
/**
* Depth of the current token in the JSON.
*
* - 0 = finished with document
* - 1 = document root value (could be [ or {, not yet known)
* - 2 = , or } inside root array/object
* - 3 = key or value inside root array/object.
*/
depth_t _depth{};
/**
* Beginning of the document indexes.
* Normally we have root == parser->implementation->structural_indexes.get()
* but this may differ, especially in streaming mode (where we have several
* documents);
*/
token_position _root{};
/**
* Normally, a json_iterator operates over a single document, but in
* some cases, we may have a stream of documents. This attribute is meant
* as meta-data: the json_iterator works the same irrespective of the
* value of this attribute.
*/
bool _streaming{false};
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
bool _allow_incomplete_json{false};
#endif
public:
simdjson_inline json_iterator() noexcept = default;
simdjson_inline json_iterator(json_iterator &&other) noexcept;
simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept;
simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default;
simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default;
/**
* Skips a JSON value, whether it is a scalar, array or object.
*/
simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept;
/**
* Tell whether the iterator is still at the start
*/
simdjson_inline bool at_root() const noexcept;
/**
* Tell whether we should be expected to run in streaming
* mode (iterating over many documents). It is pure metadata
* that does not affect how the iterator works. It is used by
* start_root_array() and start_root_object().
*/
simdjson_inline bool streaming() const noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool allow_incomplete_json() const noexcept;
simdjson_inline size_t remaining_input_length(const uint8_t *json) const noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/**
* Get the root value iterator
*/
simdjson_inline token_position root_position() const noexcept;
/**
* Assert that we are at the document depth (== 1)
*/
simdjson_inline void assert_at_document_depth() const noexcept;
/**
* Assert that we are at the root of the document
*/
simdjson_inline void assert_at_root() const noexcept;
/**
* Tell whether the iterator is at the EOF mark
*/
simdjson_inline bool at_end() const noexcept;
/**
* Tell whether the iterator is live (has not been moved).
*/
simdjson_inline bool is_alive() const noexcept;
/**
* Abandon this iterator, setting depth to 0 (as if the document is finished).
*/
simdjson_inline void abandon() noexcept;
/**
* Advance the current token without modifying depth.
*/
simdjson_inline const uint8_t *return_current_and_advance() noexcept;
/**
* Returns true if there is a single token in the index (i.e., it is
* a JSON with a scalar value such as a single number).
*
* @return whether there is a single token
*/
simdjson_inline bool is_single_token() const noexcept;
/**
* Assert that there are at least the given number of tokens left.
*
* Has no effect in release builds.
*/
simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept;
/**
* Assert that the given position addresses an actual token (is within bounds).
*
* Has no effect in release builds.
*/
simdjson_inline void assert_valid_position(token_position position) const noexcept;
/**
* Get the JSON text for a given token (relative).
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token.
*/
simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept;
/**
* Get a pointer to the current location in the input buffer.
*
* This is not null-terminated; it is a view into the JSON.
*
* You may be pointing outside of the input buffer: it is not generally
* safe to dereference this pointer.
*/
simdjson_inline const uint8_t *unsafe_pointer() const noexcept;
/**
* Get the JSON text for a given token.
*
* This is not null-terminated; it is a view into the JSON.
*
* @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 is not used ...
*/
simdjson_inline const uint8_t *peek(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current token (or relative).
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_length(token_position position) const noexcept;
/**
* Get the maximum length of the JSON text for the current root token.
*
* The length will include any whitespace at the end of the token.
*
* @param position The position of the token to retrieve.
*/
simdjson_inline uint32_t peek_root_length(token_position position) const noexcept;
/**
* Get the JSON text for the last token in the document.
*
* 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 is not used ...
*/
simdjson_inline const uint8_t *peek_last() const noexcept;
/**
* Ascend one level.
*
* Validates that the depth - 1 == parent_depth.
*
* @param parent_depth the expected parent depth.
*/
simdjson_inline void ascend_to(depth_t parent_depth) noexcept;
/**
* Descend one level.
*
* Validates that the new depth == child_depth.
*
* @param child_depth the expected child depth.
*/
simdjson_inline void descend_to(depth_t child_depth) noexcept;
simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept;
/**
* Get current depth.
*/
simdjson_inline depth_t depth() const noexcept;
/**
* Get current (writeable) location in the string buffer.
*/
simdjson_inline uint8_t *&string_buf_loc() noexcept;
/**
* Report an unrecoverable error, preventing further iteration.
*
* @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
/**
* Log error, but don't stop iteration.
* @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD.
* @param message An error message to report with the error.
*/
simdjson_warn_unused simdjson_inline error_code optional_error(error_code error, const char *message) noexcept;
/**
* Take an input in json containing max_len characters and attempt to copy it over to tmpbuf, a buffer with
* N bytes of capacity. It will return false if N is too small (smaller than max_len) of if it is zero.
* The buffer (tmpbuf) is padded with space characters.
*/
simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept;
simdjson_inline token_position position() const noexcept;
/**
* Write the raw_json_string to the string buffer and return a string_view.
* Each raw_json_string should be unescaped once, or else the string buffer might
* overflow.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, bool allow_replacement) noexcept;
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in) noexcept;
simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept;
simdjson_warn_unused simdjson_inline error_code consume_character(char c) noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position start_position(depth_t depth) const noexcept;
simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept;
#endif
/* Useful for debugging and logging purposes. */
inline std::string to_string() const noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Updates this json iterator so that it is back at the beginning of the document,
* as if it had just been created.
*/
inline void rewind() noexcept;
/**
* This checks whether the {,},[,] are balanced so that the document
* ends with proper zero depth. This requires scanning the whole document
* and it may be expensive. It is expected that it will be rarely called.
* It does not attempt to match { with } and [ with ].
*/
inline bool balanced() const noexcept;
protected:
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser, bool streaming) noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/// The last token before the end
simdjson_inline token_position last_position() const noexcept;
/// The token *at* the end. This points at gibberish and should only be used for comparison.
simdjson_inline token_position end_position() const noexcept;
/// The end of the buffer.
simdjson_inline token_position end() const noexcept;
friend class document;
friend class document_stream;
friend class object;
friend class array;
friend class value;
friend class raw_json_string;
friend class parser;
friend class value_iterator;
friend class field;
template <typename... Args>
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 <typename... Args>
friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, logger::log_level level, Args&&... args) noexcept;
}; // json_iterator
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::json_iterator> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::json_iterator> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::json_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_H
/* end file simdjson/generic/ondemand/json_iterator.h for rvv_vls */
/* including simdjson/generic/ondemand/json_type.h for rvv_vls: #include "simdjson/generic/ondemand/json_type.h" */
/* begin file simdjson/generic/ondemand/json_type.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* The type of a JSON value.
*/
enum class json_type {
unknown=0,
// Start at 1 to catch uninitialized / default values more easily
array=1, ///< A JSON array ( [ 1, 2, 3 ... ] )
object, ///< A JSON object ( { "a": 1, "b" 2, ... } )
number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...)
string, ///< A JSON string ( "a" or "hello world\n" ...)
boolean, ///< A JSON boolean (true or false)
null ///< A JSON null (null)
};
/**
* A type representing a JSON number.
* The design of the struct is deliberately straight-forward. All
* functions return standard values with no error check.
*/
struct number {
/**
* return the automatically determined type of
* the number: number_type::floating_point_number,
* number_type::signed_integer or number_type::unsigned_integer.
*
* enum class number_type {
* floating_point_number=1, /// a binary64 number
* signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
* unsigned_integer /// a positive integer larger or equal to 1<<63
* };
*/
simdjson_inline ondemand::number_type get_number_type() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::unsigned_integer.
*/
simdjson_inline bool is_uint64() const noexcept;
/**
* return the value as a uint64_t, only valid if is_uint64() is true.
*/
simdjson_inline uint64_t get_uint64() const noexcept;
simdjson_inline operator uint64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::signed_integer.
*/
simdjson_inline bool is_int64() const noexcept;
/**
* return the value as a int64_t, only valid if is_int64() is true.
*/
simdjson_inline int64_t get_int64() const noexcept;
simdjson_inline operator int64_t() const noexcept;
/**
* return true if the automatically determined type of
* the number is number_type::floating_point_number.
*/
simdjson_inline bool is_double() const noexcept;
/**
* return the value as a double, only valid if is_double() is true.
*/
simdjson_inline double get_double() const noexcept;
simdjson_inline operator double() const noexcept;
/**
* Convert the number to a double. Though it always succeed, the conversion
* may be lossy if the number cannot be represented exactly.
*/
simdjson_inline double as_double() const noexcept;
protected:
/**
* The next block of declaration is designed so that we can call the number parsing
* functions on a number type. They are protected and should never be used outside
* of the core simdjson library.
*/
friend class value_iterator;
template<typename W>
friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer);
template<typename W>
friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer);
/** Store a signed 64-bit value to the number. */
simdjson_inline void append_s64(int64_t value) noexcept;
/** Store an unsigned 64-bit value to the number. */
simdjson_inline void append_u64(uint64_t value) noexcept;
/** Store a double value to the number. */
simdjson_inline void append_double(double value) noexcept;
/** Specifies that the value is a double, but leave it undefined. */
simdjson_inline void skip_double() noexcept;
/**
* End of friend declarations.
*/
/**
* Our attributes are a union type (size = 64 bits)
* followed by a type indicator.
*/
union {
double floating_point_number;
int64_t signed_integer;
uint64_t unsigned_integer;
} payload{0};
number_type type{number_type::signed_integer};
};
/**
* Write the JSON type to the output stream
*
* @param out The output stream.
* @param type The json_type.
*/
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Send JSON type to an output stream.
*
* @param out The output stream.
* @param type The json_type.
* @throw simdjson_error if the result being printed has an error. If there is an error with the
* underlying output stream, that error will be propagated (simdjson_error will not be
* thrown).
*/
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false);
#endif
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::json_type> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::json_type> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::json_type &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_H
/* end file simdjson/generic/ondemand/json_type.h for rvv_vls */
/* including simdjson/generic/ondemand/raw_json_string.h for rvv_vls: #include "simdjson/generic/ondemand/raw_json_string.h" */
/* begin file simdjson/generic/ondemand/raw_json_string.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* A string escaped per JSON rules, terminated with quote ("). They are used to represent
* unescaped keys inside JSON documents.
*
* (In other words, a pointer to the beginning of a string, just after the start quote, inside a
* JSON file.)
*
* This class is deliberately simplistic and has little functionality. You can
* compare a raw_json_string instance with an unescaped C string, but
* that is nearly all you can do.
*
* The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own
* buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser
* instance. Doing so requires you to have a sufficiently large buffer.
*
* The raw_json_string instances originate typically from field instance which in turn represent
* key-value pairs from object instances. From a field instance, you get the raw_json_string
* instance by calling key(). You can, if you want a more usable string_view instance, call
* the unescaped_key() method on the field instance. You may also create a raw_json_string from
* any other string value, with the value.get_raw_json_string() method. Again, you can get
* a more usable string_view instance by calling get_string().
*
*/
class raw_json_string {
public:
/**
* Create a new invalid raw_json_string.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline raw_json_string() noexcept = default;
/**
* Create a new invalid raw_json_string pointed at the given location in the JSON.
*
* The given location must be just *after* the beginning quote (") in the JSON file.
*
* It *must* be terminated by a ", and be a valid JSON string.
*/
simdjson_inline raw_json_string(const uint8_t * _buf) noexcept;
/**
* Get the raw pointer to the beginning of the string in the JSON (just after the ").
*
* It is possible for this function to return a null pointer if the instance
* has outlived its existence.
*/
simdjson_inline const char * raw() const noexcept;
/**
* Get the character at index i. This is unchecked.
* [0] when the string is of length 0 returns the final quote (").
*/
simdjson_inline char operator[](size_t i) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done) on target.size() characters,
* and if the raw_json_string instance has a quote character at byte index target.size().
* We never read more than length + 1 bytes in the raw_json_string instance.
* If length is smaller than target.size(), this will return false.
*
* The std::string_view instance may contain any characters. However, the caller
* is responsible for setting length so that length bytes may be read in the
* raw_json_string.
*
* Performance: the comparison may be done using memcmp which may be efficient
* for long strings.
*/
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The std::string_view instance should not contain unescaped quote characters:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* Performance: the comparison is done byte-by-byte which might be inefficient for
* long strings.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
* The provided C string should not contain an unescaped quote character:
* the caller is responsible for this check. See is_free_from_unescaped_quote.
*
* If target is a compile-time constant, and your compiler likes you,
* you should be able to do the following without performance penalty...
*
* static_assert(raw_json_string::is_free_from_unescaped_quote(target), "");
* s.unsafe_is_equal(target);
*/
simdjson_inline bool unsafe_is_equal(const char* target) const noexcept;
/**
* This compares the current instance to the std::string_view target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(std::string_view target) const noexcept;
/**
* This compares the current instance to the C string target: returns true if
* they are byte-by-byte equal (no escaping is done).
*/
simdjson_inline bool is_equal(const char* target) const noexcept;
/**
* Returns true if target is free from unescaped quote. If target is known at
* compile-time, we might expect the computation to happen at compile time with
* many compilers (not all!).
*/
static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept;
static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept;
private:
/**
* This will set the inner pointer to zero, effectively making
* this instance unusable.
*/
simdjson_inline void consume() noexcept { buf = nullptr; }
/**
* Checks whether the inner pointer is non-null and thus usable.
*/
simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; }
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result will be a valid UTF-8.
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
* @param allow_replacement Whether we allow replacement of invalid surrogate pairs.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(json_iterator &iter, bool allow_replacement) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc.
* The result may not be a valid UTF-8. https://simonsapin.github.io/wtf-8/
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid until the next parse() call on the parser.
*
* @param iter A json_iterator, which contains a buffer where the string will be written.
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(json_iterator &iter) const noexcept;
const uint8_t * buf{};
friend class object;
friend class field;
friend class parser;
friend struct simdjson_result<raw_json_string>;
};
simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept;
/**
* Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible
* for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings.
*/
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept;
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept;
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept;
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::raw_json_string> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::raw_json_string> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::raw_json_string &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline ~simdjson_result() noexcept = default; ///< @private
simdjson_inline simdjson_result<const char *> raw() const noexcept;
simdjson_inline char operator[](size_t) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape(rvv_vls::ondemand::json_iterator &iter, bool allow_replacement) const noexcept;
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescape_wobbly(rvv_vls::ondemand::json_iterator &iter) const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_H
/* end file simdjson/generic/ondemand/raw_json_string.h for rvv_vls */
/* including simdjson/generic/ondemand/parser.h for rvv_vls: #include "simdjson/generic/ondemand/parser.h" */
/* begin file simdjson/generic/ondemand/parser.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <thread>
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* The default batch size for document_stream instances for this On-Demand kernel.
* Note that different On-Demand kernel may use a different DEFAULT_BATCH_SIZE value
* in the future.
*/
static constexpr size_t DEFAULT_BATCH_SIZE = 1000000;
/**
* Some adversary might try to set the batch size to 0 or 1, which might cause problems.
* We set a minimum of 32B since anything else is highly likely to be an error. In practice,
* most users will want a much larger batch size.
*
* All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON
* document can ever span 0 or 1 byte and that very large values would create memory allocation issues.
*/
static constexpr size_t MINIMAL_BATCH_SIZE = 32;
/**
* A JSON fragment iterator.
*
* This holds the actual iterator as well as the buffer for writing strings.
*/
class parser {
public:
/**
* Create a JSON parser.
*
* The new parser will have zero capacity.
*/
inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept;
inline parser(parser &&other) noexcept = default;
simdjson_inline parser(const parser &other) = delete;
simdjson_inline parser &operator=(const parser &other) = delete;
simdjson_inline parser &operator=(parser &&other) noexcept = default;
/** Deallocate the JSON parser. */
inline ~parser() noexcept = default;
/**
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* document doc = parser.iterate(json);
*
* It is expected that the content is a valid UTF-8 file, containing a valid JSON document.
* Otherwise the iterate method may return an error. In particular, the whole input should be
* valid: we do not attempt to tolerate incorrect content either before or after a JSON
* document. If there is a UTF-8 BOM, the parser skips it.
*
* ### IMPORTANT: Validate what you use
*
* Calling iterate on an invalid JSON document may not immediately trigger an error. The call to
* iterate does not parse and validate the whole document.
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* ### std::string references
*
* If you pass a mutable std::string reference (std::string&), the parser will seek to extend
* its capacity to SIMDJSON_PADDING bytes beyond the end of the string.
*
* Whenever you pass an std::string reference, the parser will access the bytes beyond the end of
* the string but before the end of the allocated memory (std::string::capacity()).
* If you are using a sanitizer that checks for reading uninitialized bytes or std::string's
* container-overflow checks, you may encounter sanitizer warnings.
* You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the
* string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view
* which can be be passed to the parser's iterate function:
*
* std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )";
* document doc = parser.iterate(simdjson::pad(json));
*
* @param json The JSON to parse.
* @param len The length of the JSON.
* @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING).
*
* @return The document, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<document> iterate(padded_string_view json) & noexcept;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_result<document> iterate_allow_incomplete_json(padded_string_view json) & noexcept;
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const char *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(std::string_view json, size_t capacity) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept
The string instance might be have its capacity extended. Note that this can still
result in AddressSanitizer: container-overflow in some cases. */
simdjson_warn_unused simdjson_result<document> iterate(std::string &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(const simdjson_result<padded_string_view> &json) & noexcept;
/** @overload simdjson_result<document> iterate(padded_string_view json) & noexcept */
simdjson_warn_unused simdjson_result<document> iterate(padded_string &&json) & noexcept = delete;
/**
* @private
*
* Start iterating an on-demand JSON document.
*
* ondemand::parser parser;
* json_iterator doc = parser.iterate(json);
*
* ### IMPORTANT: Buffer Lifetime
*
* Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as
* long as the document iteration.
*
* ### IMPORTANT: Document Lifetime
*
* Only one iteration at a time can happen per parser, and the parser *must* be kept alive during
* iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before
* you call parse() again or destroy the parser.
*
* The ondemand::document instance holds the iterator. The document must remain in scope
* while you are accessing instances of ondemand::value, ondemand::object, ondemand::array.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* @param json The JSON to parse.
*
* @return The iterator, or an error:
* - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes.
* - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory
* allocation fails.
* - EMPTY if the document is all whitespace.
* - UTF8_ERROR if the document is not valid UTF-8.
* - UNESCAPED_CHARS if a string contains control characters that must be escaped
* - UNCLOSED_STRING if there is an unclosed string in the document.
*/
simdjson_warn_unused simdjson_result<json_iterator> iterate_raw(padded_string_view json) & noexcept;
/**
* Parse a buffer containing many JSON documents.
*
* auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded;
* ondemand::parser parser;
* ondemand::document_stream docs = parser.iterate_many(json);
* for (auto & doc : docs) {
* std::cout << doc["foo"] << std::endl;
* }
* // Prints 1 2 3
*
* No copy of the input buffer is made.
*
* The function is lazy: it may be that no more than one JSON document at a time is parsed.
*
* The caller is responsabile to ensure that the input string data remains unchanged and is
* not deleted during the loop.
*
* ### Format
*
* The buffer must contain a series of one or more JSON documents, concatenated into a single
* buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document,
* then starts parsing the next document at that point. (It does this with more parallelism and
* lookahead than you might think, though.)
*
* documents that consist of an object or array may omit the whitespace between them, concatenating
* with no separator. Documents that consist of a single primitive (i.e. documents that are not
* arrays or objects) MUST be separated with ASCII whitespace.
*
* The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8).
* If there is a UTF-8 BOM, the parser skips it.
*
* The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse.
* Setting batch_size to excessively large or excessively small values may impact negatively the
* performance.
*
* ### Threads
*
* When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the
* hood to do some lookahead.
*
* ### REQUIRED: Buffer Padding
*
* The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what
* those bytes are initialized to, as long as they are allocated. These bytes will be read: if you
* using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the
* SIMDJSON_PADDING bytes to avoid runtime warnings.
*
* This is checked automatically with all iterate_many function calls, except for the two
* that take pointers (const char* or const uint8_t*).
*
* ### Threads
*
* ### Parser Capacity
*
* If the parser's current capacity is less than batch_size, it will allocate enough capacity
* to handle it (up to max_capacity).
*
* @param buf The concatenated JSON to parse.
* @param len The length of the concatenated JSON.
* @param batch_size The batch size to use. MUST be larger than the largest document. The sweet
* spot is cache-related: small enough to fit in cache, yet big enough to
* parse as many documents as possible in one tight loop.
* Defaults to 1MB, which has been a reasonable sweet spot in our tests.
* @param allow_comma_separated @deprecated Use stream_format::comma_delimited instead.
* When true, maps internally to stream_format::comma_delimited.
* Defaults to false.
* @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors:
* - MEMALLOC if the parser does not have enough capacity and memory allocation fails
* - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity.
* - other json errors if parsing fails. You should not rely on these errors to always the same for the
* same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware).
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size)
the string might be automatically padded with up to SIMDJSON_PADDING whitespace characters */
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept;
/** @private An rvalue input is destroyed at the end of the full-expression, while the
* returned document_stream only holds a pointer to it: iterating the stream would then
* read freed memory. These deleted overloads also catch a std::string_view argument,
* which would otherwise convert implicitly to a padded_string temporary. */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size = DEFAULT_BATCH_SIZE) = delete;// unsafe
/** @private We do not want to allow implicit conversion from C string to std::string. */
simdjson_result<document_stream> iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete;
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
/**
* @deprecated Use iterate_many with stream_format::comma_delimited instead.
*/
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(padded_string_view json, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) */
simdjson_deprecated inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, bool allow_comma_separated) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, bool allow_comma_separated) = delete;// unsafe
#endif // SIMDJSON_DISABLE_DEPRECATED_API
/**
* Parse a stream of JSON documents with explicit format specification.
*
* @param buf The concatenated JSON documents.
* @param len The length of the buffer.
* @param batch_size The batch size to use.
* @param format The stream format (whitespace_delimited, json_sequence, or comma_delimited).
* @return A stream of documents, or an error.
*/
inline simdjson_result<document_stream> iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format)
*
* The string is padded in place if needed (see simdjson::pad), as with iterate_many(std::string &s, size_t batch_size).
*/
inline simdjson_result<document_stream> iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept;
/** @overload iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept;
/** @private Deleted for the same reason as iterate_many(const std::string &&s, size_t batch_size). */
inline simdjson_result<document_stream> iterate_many(const std::string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** @private @overload iterate_many(const std::string &&s, size_t batch_size, stream_format format) */
inline simdjson_result<document_stream> iterate_many(const padded_string &&s, size_t batch_size, stream_format format) = delete;// unsafe
/** The capacity of this parser (the largest document it can process). */
simdjson_pure simdjson_inline size_t capacity() const noexcept;
/** The maximum capacity of this parser (the largest document it is allowed to process). */
simdjson_pure simdjson_inline size_t max_capacity() const noexcept;
simdjson_inline void set_max_capacity(size_t max_capacity) noexcept;
/**
* The maximum depth of this parser (the most deeply nested objects and arrays it can process).
* This parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*/
simdjson_pure simdjson_inline size_t max_depth() const noexcept;
/**
* Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length
* and `max_depth` depth.
*
* The max_depth parameter is only relevant when the macro SIMDJSON_DEVELOPMENT_CHECKS is set to true.
* The document's instance current_depth() method should be used to monitor the parsing
* depth and limit it if desired.
*
* @param capacity The new capacity.
* @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH.
* @return The error, if there is one.
*/
simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept;
#ifdef SIMDJSON_THREADS_ENABLED
/**
* The parser instance can use threads when they are available to speed up some
* operations. It is enabled by default. Changing this attribute will change the
* behavior of the parser for future operations.
*/
bool threaded{true};
#else
/**
* When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads.
*/
bool threaded{false};
#endif
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result must be valid UTF-8.
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @param allow_replacement Whether we allow a replacement if the input string contains unmatched surrogate pairs.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement = false) const noexcept;
/**
* Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer.
* The result may not be valid UTF-8. See https://simonsapin.github.io/wtf-8/
* The provided pointer is advanced to the end of the string by reference, and a string_view instance
* is returned. You can ensure that your buffer is large enough by allocating a block of memory at least
* as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer.
*
* This unescape function is a low-level function. If you want a more user-friendly approach, you should
* avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string()
* instead of get_raw_json_string()).
*
* ## IMPORTANT: string_view lifetime
*
* The string_view is only valid as long as the bytes in dst.
*
* @param raw_json_string input
* @param dst A pointer to a buffer at least large enough to write this string as well as
* an additional SIMDJSON_PADDING bytes.
* @return A string_view pointing at the unescaped string in dst
* @error STRING_ERROR if escapes are incorrect.
*/
simdjson_inline simdjson_result<std::string_view> unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept;
#if SIMDJSON_DEVELOPMENT_CHECKS
/**
* Returns true if string_buf_loc is outside of the allocated range for the
* the string buffer. When true, it indicates that the string buffer has overflowed.
* This is a development-time check that is not needed in production. It can be
* used to detect buffer overflows in the string buffer and usafe usage of the
* string buffer.
*/
bool string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept;
#endif
/**
* Get a unique parser instance corresponding to the current thread.
* This instance can be safely used within the current thread, but it should
* not be passed to other threads.
*
* A parser should only be used for one document at a time.
*
* Our simdjson::from functions use this parser instance.
*
* You can free the related parser by calling release_parser().
*/
static simdjson_inline simdjson_warn_unused ondemand::parser& get_parser();
/**
* Release the parser instance initialized by get_parser() and all the
* associated resources (memory). Returns true if a parser instance
* was released.
*/
static simdjson_inline bool release_parser();
private:
/** Get the thread-local parser instance, allocates it if needed */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_parser_instance();
/** Get the thread-local parser instance, it might be null */
static simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& get_threadlocal_parser_if_exists();
/** @private [for benchmarking access] The implementation to use */
std::unique_ptr<simdjson::internal::dom_parser_implementation> implementation{};
size_t _capacity{0};
size_t _max_capacity;
size_t _max_depth{DEFAULT_MAX_DEPTH};
size_t _document_len{0};
std::unique_ptr<uint8_t[]> string_buf{};
#if SIMDJSON_DEVELOPMENT_CHECKS
std::unique_ptr<token_position[]> start_positions{};
#endif
friend class json_iterator;
friend class document_stream;
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::parser> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::parser> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::parser &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_H
/* end file simdjson/generic/ondemand/parser.h for rvv_vls */
// All other declarations
/* including simdjson/generic/ondemand/array.h for rvv_vls: #include "simdjson/generic/ondemand/array.h" */
/* begin file simdjson/generic/ondemand/array.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* A forward-only JSON array.
*/
class array {
public:
/**
* Create a new invalid array.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline array() noexcept = default;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this array while it is
* alive, so that reentrant access (at(), count_elements(), reset(), ...) is
* reported as OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Begin iteration over a temporary array, e.g., `v.get_array().begin()`.
*
* The iterator does not depend on the array instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<array_iterator> begin() && noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() noexcept;
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*
* To check that an array is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the beginning of the array and checks whether the
* array is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
simdjson_inline simdjson_result<bool> is_empty() & noexcept;
/**
* Reset the iterator so that we are pointing back at the
* beginning of the array. You should still consume values only once even if you
* can iterate through the array more than once. If you unescape a string
* within the array more than once, you have unsafe code. Note that rewinding
* an array means that you may need to reparse it anew: it is not a free
* operation.
*
* @returns true if the array contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/0/foo/a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an array
* instance: there is no rewind and no invalidation.
*
* You may only call at_pointer on an array after it has been created, but before it has
* been first accessed. When calling at_pointer on an array, the pointer is advanced to
* the location indicated by the JSON pointer (in case of success). It is no longer possible
* to call at_pointer on the same array.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* @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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like "[*]" to match all array elements.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
/**
* Consumes the array and returns a string_view instance corresponding to the
* array as represented in JSON. It points inside the original document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
/**
* Get the value at the given index. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this array as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON array is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, array>) {
static_assert(custom_deserializable<T, array>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, array>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the array, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
/**
* Begin array iteration.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
*/
static simdjson_inline simdjson_result<array> start(value_iterator &iter) noexcept;
/**
* Begin array iteration from the root.
*
* @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the
* resulting array.
* @error INCORRECT_TYPE if the iterator is not at [.
* @error TAPE_ERROR if there is no closing ] at the end of the document.
*/
static simdjson_inline simdjson_result<array> start_root(value_iterator &iter) noexcept;
/**
* Begin array iteration.
*
* This version of the method should be called after the initial [ has been verified, and is
* intended for use by switch statements that check the type of a value.
*
* @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array.
*/
static simdjson_inline simdjson_result<array> started(value_iterator &iter) noexcept;
/**
* Create an array at the given Internal array creation. Call array::start() or array::started() instead of this.
*
* @param iter The iterator. Must either be at the start of the first element with iter.is_alive()
* == true, or past the [] with is_alive() == false if the array is empty. Will be *moved*
* into the resulting array.
*/
simdjson_inline array(const value_iterator &iter) noexcept;
/**
* Iterator marking current position.
*
* iter.is_alive() == false indicates iteration is complete.
*/
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<value>;
friend struct simdjson_result<array>;
friend class array_iterator;
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::array> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::array> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::array &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> begin() && noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> end() noexcept;
inline simdjson_result<size_t> count_elements() & noexcept;
inline simdjson_result<bool> is_empty() & noexcept;
inline simdjson_result<bool> reset() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at(size_t index) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, rvv_vls::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, rvv_vls::ondemand::array>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, rvv_vls::ondemand::array>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, rvv_vls::ondemand::array>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_H
/* end file simdjson/generic/ondemand/array.h for rvv_vls */
/* including simdjson/generic/ondemand/array_iterator.h for rvv_vls: #include "simdjson/generic/ondemand/array_iterator.h" */
/* begin file simdjson/generic/ondemand/array_iterator.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H */
/* amalgamation skipped (editor-only): #include <iterator> */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* A forward-only JSON array.
*
* This is an input_iterator, meaning:
* - It is forward-only
* - * must be called at most once per element.
* - ++ must be called exactly once in between each * (*, ++, *, ++, * ...)
*/
class array_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
/** Create a new, invalid array iterator. */
simdjson_inline array_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~array_iterator() noexcept;
simdjson_inline array_iterator(array_iterator&&) noexcept;
simdjson_inline array_iterator& operator=(array_iterator&&) noexcept;
simdjson_inline array_iterator(const array_iterator&) noexcept;
simdjson_inline array_iterator& operator=(const array_iterator&) noexcept;
#endif
//
// Iterator interface
//
/**
* Get the current element.
*
* Part of the std::iterator interface.
*/
simdjson_inline simdjson_result<value>
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
/**
* Check if we are at the end of the JSON.
*
* Part of the std::iterator interface.
*
* @return true if there are no more elements in the JSON array.
*/
simdjson_inline bool operator==(const array_iterator &) const noexcept;
/**
* Check if there are more elements in the JSON array.
*
* Part of the std::iterator interface.
*
* @return true if there are more elements in the JSON array.
*/
simdjson_inline bool operator!=(const array_iterator &) const noexcept;
/**
* Move to the next element.
*
* Part of the std::iterator interface.
*/
simdjson_inline array_iterator &operator++() noexcept;
/**
* Check if the array is at the end.
*/
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
array* parent{nullptr};
simdjson_inline array_iterator(const value_iterator &_iter, array* _parent) noexcept;
#endif
value_iterator iter{};
simdjson_inline array_iterator(const value_iterator &iter) noexcept;
friend class array;
friend class value;
friend struct simdjson_result<array_iterator>;
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::array_iterator> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::array_iterator> {
using iterator_category = std::input_iterator_tag;
using value_type = simdjson_result<rvv_vls::ondemand::value>;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = value_type;
simdjson_inline simdjson_result(rvv_vls::ondemand::array_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline bool operator==(const simdjson_result<rvv_vls::ondemand::array_iterator> &) const noexcept;
simdjson_inline bool operator!=(const simdjson_result<rvv_vls::ondemand::array_iterator> &) const noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> &operator++() noexcept;
simdjson_warn_unused simdjson_inline bool at_end() const noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_H
/* end file simdjson/generic/ondemand/array_iterator.h for rvv_vls */
/* including simdjson/generic/ondemand/document.h for rvv_vls: #include "simdjson/generic/ondemand/document.h" */
/* begin file simdjson/generic/ondemand/document.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* A JSON document. It holds a json_iterator instance.
*
* Used by tokens to get text, and string buffer location.
*
* You must keep the document around during iteration.
*/
class document {
public:
/**
* Create a new invalid document.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline document() noexcept = default;
simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy
simdjson_inline document(document &&other) noexcept = default;
simdjson_inline document &operator=(const document &other) noexcept = delete;
simdjson_inline document &operator=(document &&other) noexcept = default;
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @returns INCORRECT_TYPE If the JSON value is not an array.
*/
simdjson_inline simdjson_result<array> get_array() & noexcept;
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @returns INCORRECT_TYPE If the JSON value is not an object.
*/
simdjson_inline simdjson_result<object> get_object() & noexcept;
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
/**
* Cast this JSON value (inside string) to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer.
*/
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
/**
* Cast this JSON value (inside string) to a signed integer.
*
* @returns A signed 64-bit integer.
* @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer.
*/
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
/**
* Cast this JSON value to a 32-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint32_t.
*
* @returns A 32-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint32_t.
*/
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
/**
* Cast this JSON value to a 32-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int32_t.
*
* @returns A 32-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int32_t.
*/
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
/**
* Cast this JSON value to a 16-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint16_t.
*
* @returns A 16-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint16_t.
*/
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
/**
* Cast this JSON value to a 16-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int16_t.
*
* @returns A 16-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int16_t.
*/
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
/**
* Cast this JSON value to an 8-bit unsigned integer.
*
* Calls get_uint64() and checks that the result fits in a uint8_t.
*
* @returns An 8-bit unsigned integer.
* @returns INCORRECT_TYPE If the JSON value is not an unsigned integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in a uint8_t.
*/
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
/**
* Cast this JSON value to an 8-bit signed integer.
*
* Calls get_int64() and checks that the result fits in an int8_t.
*
* @returns An 8-bit signed integer.
* @returns INCORRECT_TYPE If the JSON value is not an integer.
* @returns NUMBER_OUT_OF_RANGE If the value does not fit in an int8_t.
*/
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double() noexcept;
/**
* Cast this JSON value (inside string) to a double.
*
* @returns A double.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
/**
* Cast this JSON value to a float (binary32).
*
* The value is rounded directly to binary32: it is the float nearest to the
* JSON number. Note that this may differ from get_double() followed by a cast
* to float, which rounds twice.
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float() noexcept;
/**
* Cast this JSON value (inside string) to a float (binary32).
*
* @returns A float.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
/**
* Cast this JSON value to a std::float32_t (C++23).
*
* Same as get_float(): the value is rounded directly to binary32.
*
* @returns A std::float32_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
* @returns NUMBER_ERROR If the JSON number is too large in magnitude to be a finite float.
*/
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a std::float64_t (C++23).
*
* Same as get_double().
*
* @returns A std::float64_t.
* @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number.
*/
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: Calling get_string() twice on the same document is an error.
*
* @param Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Cast this JSON value to a C++20 UTF-8 string.
*
* The string is guaranteed to be valid UTF-8.
*
* Equivalent to get<std::u8string_view>(). This is a zero-copy alias of
* get_string(): the very same bytes are returned, viewed as char8_t.
*
* Important: Calling get_u8string() twice on the same document is an error.
*
* @param allow_replacement Whether to allow a replacement character for unmatched surrogate pairs.
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Attempts to fill the provided std::string reference with the parsed value of the current string.
*
* The string is guaranteed to be valid UTF-8.
*
* Important: a value should be consumed once. Calling get_string() twice on the same value
* is an error.
*
* Performance: This method may be slower than get_string() or get_string(bool) because it may need to allocate memory.
* We recommend you avoid allocating an std::string unless you need to.
*
* @returns INCORRECT_TYPE if the JSON value is not a string. Otherwise, we return SUCCESS.
*/
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Cast this JSON value to a string.
*
* The string is not guaranteed to be valid UTF-8. See https://simonsapin.github.io/wtf-8/
*
* Important: Calling get_wobbly_string() twice on the same document is an error.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @returns INCORRECT_TYPE if the JSON value is not a string.
*/
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @returns INCORRECT_TYPE if the JSON value is not true or false.
*/
simdjson_inline simdjson_result<bool> get_bool() noexcept;
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is set to 1 (which is the case when building in Debug mode
* by default), and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value if a JSON array or object cannot be found.
* @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline simdjson_result<value> get_value() noexcept;
/**
* Checks if this JSON value is null. If and only if the value is
* null, then it is consumed (we advance). If we find a token that
* begins with 'n' but is not 'null', then an error is returned.
*
* @returns Whether the value is null.
* @returns INCORRECT_TYPE If the JSON value begins with 'n' and is not 'null'.
*/
simdjson_inline simdjson_result<bool> is_null() noexcept;
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool
*
* You may use get_double(), get_float(), get_bool(), get_uint64(), get_int64(),
* get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(),
* get_float32(), get_float64(),
* get_object(), get_array(), get_raw_json_string(), or get_string() instead.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
/**
* @overload template<typename T> simdjson_result<T> get() & noexcept
*
* We disallow the use tag_invoke CPO on a moved document; it may create UB
* if user uses `ondemand::array` or `ondemand::object` in their custom type.
*
* The member function is still remains specialize-able for compatibility
* reasons, but we completely disallow its use when a tag_invoke customization
* is provided.
*/
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_deprecated simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
/**
* Cast this JSON value to an instance of type T. The programmer is responsible for
* providing an implementation of get<T> for the type T, if T is not one of the types
* supported by the library (object, array, raw_json_string, string_view, uint64_t, etc.)
*
* See https://github.com/simdjson/simdjson/blob/master/doc/basics.md#adding-support-for-custom-types
*
* @returns An instance of type T
*/
template <class T>
explicit simdjson_inline operator T() & noexcept(false);
template <class T>
explicit simdjson_deprecated simdjson_inline operator T() && noexcept(false);
/**
* Cast this JSON value to an array.
*
* @returns An object that can be used to iterate the array.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array.
*/
simdjson_inline operator array() & noexcept(false);
/**
* Cast this JSON value to an object.
*
* @returns An object that can be used to look up or iterate fields.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object.
*/
simdjson_inline operator object() & noexcept(false);
/**
* Cast this JSON value to an unsigned integer.
*
* @returns A unsigned 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer.
*/
explicit simdjson_inline operator uint64_t() noexcept(false);
/**
* Cast this JSON value to a signed integer.
*
* @returns A signed 64-bit integer.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer.
*/
explicit simdjson_inline operator int64_t() noexcept(false);
/**
* Cast this JSON value to a double.
*
* @returns A double.
* @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number.
*/
explicit simdjson_inline operator double() noexcept(false);
/**
* Cast this JSON value to a string.
*
* The string is guaranteed to be valid UTF-8.
*
* @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next
* time it parses a document or when it is destroyed.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator std::string_view() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a raw_json_string.
*
* The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n).
*
* @returns A pointer to the raw JSON for the given string.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string.
*/
explicit simdjson_inline operator raw_json_string() noexcept(false) simdjson_lifetime_bound;
/**
* Cast this JSON value to a bool.
*
* @returns A bool value.
* @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false.
*/
explicit simdjson_inline operator bool() noexcept(false);
/**
* Cast this JSON value to a value when the document is an object or an array.
*
* You must not have begun iterating through the object or array. When
* SIMDJSON_DEVELOPMENT_CHECKS is defined, and you have already begun iterating,
* you will get an OUT_OF_ORDER_ITERATION error. If you have begun iterating, you can use
* rewind() to reset the document to its initial state before calling this method.
*
* @returns A value value if a JSON array or object cannot be found.
* @exception SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function).
*/
simdjson_inline operator value() noexcept(false);
#endif
/**
* This method scans the array and counts the number of elements.
* The count_elements method should always be called before you have begun
* iterating through the array: it is expected that you are pointing at
* the beginning of the array.
* The runtime complexity is linear in the size of the array. After
* calling this function, if successful, the array is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue. Note that count_elements() does not validate the JSON values,
* only the structure of the array.
*/
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Get the value at the given index in the array. This function has linear-time complexity.
* This function should only be called once on an array instance since the array iterator is not reset between each call.
*
* @return The value at the given index, or:
* - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length
*/
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
/**
* Begin array iteration.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
/**
* Sentinel representing the end of the array.
*
* Part of the std::iterable interface.
*/
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded);
* double z = obj.find_field("z");
* double y = obj.find_field("y");
* double x = obj.find_field("x");
* ```
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to
* a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. E.g., the array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
/**
* Get the type of this JSON value. It does not validate or consume the value.
* E.g., you must still call "is_null()" to check that a value is null even if
* "type()" returns json_type::null.
*
* The answer can be one of
* simdjson::ondemand::json_type::object,
* simdjson::ondemand::json_type::array,
* simdjson::ondemand::json_type::string,
* simdjson::ondemand::json_type::number,
* simdjson::ondemand::json_type::boolean,
* simdjson::ondemand::json_type::null.
*
* Starting with simdjson 4.0, this function will return simdjson::ondemand::json_type::unknown
* given a bad token.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*
* NOTE: If you're only expecting a value to be one type (a typical case), it's generally
* better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just
* let it throw an exception).
*
* Prior to simdjson 4.0, this function would return an error given a bad token.
* Starting with simdjson 4.0, it will return simdjson::ondemand::json_type::unknown.
* This allows you to identify a case such as {"key": NaN} and identify the NaN value.
* The simdjson::ondemand::json_type::unknown value should only happen with non-valid JSON.
*/
simdjson_inline simdjson_result<json_type> type() noexcept;
/**
* Checks whether the document is a scalar (string, number, null, Boolean).
* Returns false when there it is an array or object.
*
* @returns true if the type is string, number, null, Boolean
* @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
*/
simdjson_inline simdjson_result<bool> 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<bool> is_string() noexcept;
/**
* Checks whether the document is a negative number.
*
* @returns true if the number if negative.
*/
simdjson_inline bool is_negative() noexcept;
/**
* Checks whether the document is an integer number. Note that
* this requires to partially parse the number string. If
* the value is determined to be an integer, it may still
* not parse properly as an integer in subsequent steps
* (e.g., it might overflow).
*
* @returns true if the number if negative.
*/
simdjson_inline simdjson_result<bool> is_integer() noexcept;
/**
* Determine the number type (integer or floating-point number) as quickly
* as possible. This function does not fully validate the input. It is
* useful when you only need to classify the numbers, without parsing them.
*
* If you are planning to retrieve the value or you need full validation,
* consider using the get_number() method instead: it will fully parse
* and validate the input, and give you access to the type:
* get_number().get_number_type().
*
* get_number_type() is number_type::unsigned_integer if we have
* an integer greater or equal to 9223372036854775808 and no larger than 18446744073709551615.
* get_number_type() is number_type::signed_integer if we have an
* integer that is less than 9223372036854775808 and greater or equal to -9223372036854775808.
* get_number_type() is number_type::big_integer if we have an integer outside
* of those ranges (either larger than 18446744073709551615 or smaller than -9223372036854775808).
* Otherwise, get_number_type() has value number_type::floating_point_number
*
* This function requires processing the number string, but it is expected
* to be faster than get_number().get_number_type() because it is does not
* parse the number value.
*
* @returns the type of the number
*/
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
/**
* Attempt to parse an ondemand::number. An ondemand::number may
* contain an integer value or a floating-point value, the simdjson
* library will autodetect the type. Thus it is a dynamically typed
* number. Before accessing the value, you must determine the detected
* type.
*
* number.get_number_type() is number_type::signed_integer if we have
* an integer in [-9223372036854775808,9223372036854775808)
* You can recover the value by calling number.get_int64() and you
* have that number.is_int64() is true.
*
* number.get_number_type() is number_type::unsigned_integer if we have
* an integer in [9223372036854775808,18446744073709551616)
* You can recover the value by calling number.get_uint64() and you
* have that number.is_uint64() is true.
*
* Otherwise, number.get_number_type() has value number_type::floating_point_number
* and we have a binary64 number.
* You can recover the value by calling number.get_double() and you
* have that number.is_double() is true.
*
* You must check the type before accessing the value: it is an error
* to call "get_int64()" when number.get_number_type() is not
* number_type::signed_integer and when number.is_int64() is false.
*/
simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
/**
* Get the raw JSON for this token.
*
* The string_view will always point into the input buffer.
*
* The string_view will start at the beginning of the token, and include the entire token
* *as well as all spaces until the next token (or EOF).* This means, for example, that a
* string token always begins with a " and is always terminated by the final ", possibly
* followed by a number of spaces.
*
* The string_view is *not* null-terminated. If this is a scalar (string, number,
* boolean, or null), the character after the end of the string_view may be the padded buffer.
*
* Tokens include:
* - {
* - [
* - "a string (possibly with UTF-8 or backslashed characters like \\\")".
* - -1.2e-100
* - true
* - false
* - null
*/
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
/**
* Reset the iterator inside the document instance so we are pointing back at the
* beginning of the document, as if it had just been created. It invalidates all
* values, objects and arrays that you have created so far (including unescaped strings).
*/
inline void rewind() noexcept;
/**
* Returns debugging information.
*/
inline std::string to_debug_string() noexcept;
/**
* Some unrecoverable error conditions may render the document instance unusable.
* The is_alive() method returns true when the document is still suitable.
*/
inline bool is_alive() noexcept;
/**
* Returns the current location in the document if in bounds.
*/
inline simdjson_result<const char *> current_location() const noexcept;
/**
* Returns true if this document has been fully parsed.
* If you have consumed the whole document and at_end() returns
* false, then there may be trailing content.
*/
inline bool at_end() const noexcept;
/**
* Returns the current depth in the document if in bounds.
*
* E.g.,
* 0 = finished with document
* 1 = document root value (could be [ or {, not yet known)
* 2 = , or } inside root array/object
* 3 = key or value inside root array/object.
*/
simdjson_inline int32_t current_depth() const noexcept;
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* 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
* should be stored in your own variables, arrays should be decoded and stored in your own array-like
* structures and so forth.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
* - 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<value> 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://www.rfc-editor.org/rfc/rfc9535 (RFC 9535)
*
* 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
*
* Supports wildcard patterns like "$.array[*]" or "$.object.*" to match multiple elements.
*
* The document will be consumed after this call.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* doc.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
protected:
/**
* Consumes the document.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
simdjson_inline document(ondemand::json_iterator &&iter) noexcept;
simdjson_inline const uint8_t *text(uint32_t idx) const noexcept;
simdjson_inline value_iterator resume_value_iterator() noexcept;
simdjson_inline value_iterator get_root_value_iterator() noexcept;
simdjson_inline simdjson_result<object> start_or_resume_object() noexcept;
static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept;
//
// Fields
//
json_iterator iter{}; ///< Current position in the document
static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0
friend class array_iterator;
friend class value;
friend class ondemand::parser;
friend class object;
friend class array;
friend class field;
friend class token;
friend class document_stream;
friend class document_reference;
};
/**
* A document_reference is a thin wrapper around a document reference instance.
* The document_reference instances are used primarily/solely for streams of JSON
* documents. They differ from document instances when parsing a scalar value
* (a document that is not an array or an object). In the case of a document,
* we expect the document to be fully consumed. In the case of a document_reference,
* we allow trailing content.
*/
class document_reference {
public:
simdjson_inline document_reference() noexcept;
simdjson_inline document_reference(document &d) noexcept;
simdjson_inline document_reference(const document_reference &other) noexcept = default;
simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default;
simdjson_inline void rewind() noexcept;
simdjson_inline simdjson_result<array> get_array() & noexcept;
simdjson_inline simdjson_result<object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template <typename T>
simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
static_assert(std::is_default_constructible<T>::value, "Cannot initialize the specified type.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
template<typename T>
simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
// Forwards to document::get<T>(), so the document customization decides.
noexcept(nothrow_gettable<T, document>)
#else
noexcept
#endif
{
static_assert(!std::is_same<T, array>::value && !std::is_same<T, object>::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour.");
return static_cast<document&>(*this).get<T>();
}
/**
* Get this value as the given type.
*
* Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float,
* std::float64_t and std::float32_t (C++23, when available), bool, value
*
* Be mindful that the document instance must remain in scope while you are accessing object, array and value instances.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON value is not an object.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>)
#else
noexcept
#endif
{
#if SIMDJSON_SUPPORTS_CONCEPTS
if constexpr (custom_deserializable<T, document_reference>) {
return deserialize(*this, out);
} else {
static_assert(!sizeof(T), "The get<T> method with type T is not implemented by the simdjson library. "
"And you do not seem to have added support for it. Indeed, we have that "
"simdjson::custom_deserializable<T> is false and the type T is not a default type "
"such as ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, or bool.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
}
#else // SIMDJSON_SUPPORTS_CONCEPTS
// Unless the simdjson library or the user provides an inline implementation, calling this method should
// immediately fail.
static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. "
"The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, "
"int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t, double, float, and bool. We recommend you use get_double(), get_float(), "
"get_bool(), get_uint64(), get_int64(), get_uint32(), get_int32(), get_uint16(), get_int16(), get_uint8(), get_int8(), "
" get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template."
" You may also add support for custom types, see our documentation.");
static_cast<void>(out); // to get rid of unused errors
return UNINITIALIZED;
#endif // SIMDJSON_SUPPORTS_CONCEPTS
}
/** @overload template<typename T> error_code get(T &out) & noexcept */
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, document_reference>);
#else
noexcept;
#endif
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
simdjson_inline operator document&() const noexcept;
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator array() & noexcept(false);
simdjson_inline operator object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<array_iterator> end() & noexcept;
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> operator[](const char *key) & noexcept;
simdjson_result<value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<number> get_number() noexcept;
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
private:
document *doc{nullptr};
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::document> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::document> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::document &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_deprecated simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
using rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::document>::operator*;
using rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::document>::operator->;
template <class T, typename std::enable_if<std::is_same<T, rvv_vls::ondemand::document>::value == false>::type>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator rvv_vls::ondemand::array() & noexcept(false);
simdjson_inline operator rvv_vls::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator rvv_vls::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator rvv_vls::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<rvv_vls::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline int32_t current_depth() const noexcept;
simdjson_inline bool at_end() const noexcept;
simdjson_inline bool is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<rvv_vls::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::document_reference> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::document_reference> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::document_reference value, error_code error) noexcept;
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline error_code rewind() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array> get_array() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::object> get_object() & noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
simdjson_inline simdjson_result<uint32_t> get_uint32() noexcept;
simdjson_inline simdjson_result<int32_t> get_int32() noexcept;
simdjson_inline simdjson_result<uint16_t> get_uint16() noexcept;
simdjson_inline simdjson_result<int16_t> get_int16() noexcept;
simdjson_inline simdjson_result<uint8_t> get_uint8() noexcept;
simdjson_inline simdjson_result<int8_t> get_int8() noexcept;
simdjson_inline simdjson_result<double> get_double() noexcept;
simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
simdjson_inline simdjson_result<float> get_float() noexcept;
simdjson_inline simdjson_result<float> get_float_in_string() noexcept;
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> get_float32() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> get_float64() noexcept;
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> get_u8string(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::raw_json_string> get_raw_json_string() noexcept;
simdjson_inline simdjson_result<bool> get_bool() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> get_value() noexcept;
simdjson_inline simdjson_result<bool> is_null() noexcept;
template<typename T> simdjson_inline simdjson_result<T> get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline simdjson_result<T> get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document_reference>);
#else
noexcept;
#endif
template<typename T> simdjson_inline error_code get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document_reference>);
#else
noexcept;
#endif
#if SIMDJSON_EXCEPTIONS
template <class T>
explicit simdjson_inline operator T() noexcept(false);
simdjson_inline operator rvv_vls::ondemand::array() & noexcept(false);
simdjson_inline operator rvv_vls::ondemand::object() & noexcept(false);
explicit simdjson_inline operator uint64_t() noexcept(false);
explicit simdjson_inline operator int64_t() noexcept(false);
explicit simdjson_inline operator double() noexcept(false);
explicit simdjson_inline operator std::string_view() noexcept(false);
explicit simdjson_inline operator rvv_vls::ondemand::raw_json_string() noexcept(false);
explicit simdjson_inline operator bool() noexcept(false);
simdjson_inline operator rvv_vls::ondemand::value() noexcept(false);
#endif
simdjson_inline simdjson_result<size_t> count_elements() & noexcept;
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at(size_t index) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> begin() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> end() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(const char *key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator[](const char *key) & noexcept;
simdjson_result<rvv_vls::ondemand::value> operator[](int) & noexcept = delete;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(const char *key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::json_type> type() noexcept;
simdjson_inline simdjson_result<bool> is_scalar() noexcept;
simdjson_inline simdjson_result<bool> is_string() noexcept;
simdjson_inline simdjson_result<const char *> current_location() noexcept;
simdjson_inline simdjson_result<int32_t> current_depth() const noexcept;
simdjson_inline simdjson_result<bool> is_negative() noexcept;
simdjson_inline simdjson_result<bool> is_integer() noexcept;
simdjson_inline simdjson_result<rvv_vls::number_type> get_number_type() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::number> get_number() noexcept;
/** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */
simdjson_inline simdjson_result<std::string_view> raw_json_token() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H
/* end file simdjson/generic/ondemand/document.h for rvv_vls */
/* including simdjson/generic/ondemand/document_stream.h for rvv_vls: #include "simdjson/generic/ondemand/document_stream.h" */
/* begin file simdjson/generic/ondemand/document_stream.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#ifdef SIMDJSON_THREADS_ENABLED
#include <thread>
#include <mutex>
#include <condition_variable>
#endif
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
/** @private Custom worker class **/
struct stage1_worker {
stage1_worker() noexcept = default;
stage1_worker(const stage1_worker&) = delete;
stage1_worker(stage1_worker&&) = delete;
stage1_worker operator=(const stage1_worker&) = delete;
~stage1_worker();
/**
* We only start the thread when it is needed, not at object construction, this may throw.
* You should only call this once.
**/
void start_thread();
/**
* Start a stage 1 job. You should first call 'run', then 'finish'.
* You must call start_thread once before.
*/
void run(document_stream * ds, parser * stage1, size_t next_batch_start);
/** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/
void finish();
private:
/**
* Normally, we would never stop the thread. But we do in the destructor.
* This function is only safe assuming that you are not waiting for results. You
* should have called run, then finish, and be done.
**/
void stop_thread();
std::thread thread{};
/** These three variables define the work done by the thread. **/
ondemand::parser * stage1_thread_parser{};
size_t _next_batch_start{};
document_stream * owner{};
/**
* We have two state variables. This could be streamlined to one variable in the future but
* we use two for clarity.
*/
bool has_work{false};
bool can_work{true};
/**
* We lock using a mutex.
*/
std::mutex locking_mutex{};
std::condition_variable cond_var{};
friend class document_stream;
};
#endif // SIMDJSON_THREADS_ENABLED
/**
* A forward-only stream of documents.
*
* Produced by parser::iterate_many.
*
*/
class document_stream {
public:
/**
* Construct an uninitialized document_stream.
*
* ```cpp
* document_stream docs;
* auto error = parser.iterate_many(json).get(docs);
* ```
*/
simdjson_inline document_stream() noexcept;
/** Move one document_stream to another. */
simdjson_inline document_stream(document_stream &&other) noexcept = default;
/** Move one document_stream to another. */
simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default;
simdjson_inline ~document_stream() noexcept;
/**
* Returns the input size in bytes.
*/
inline size_t size_in_bytes() const noexcept;
/**
* After iterating through the stream, this method
* returns the number of bytes that were not parsed at the end
* of the stream. If truncated_bytes() differs from zero,
* then the input was truncated maybe because incomplete JSON
* documents were found at the end of the stream. You
* may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()).
*
* You should only call truncated_bytes() after streaming through all
* documents, like so:
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto & doc : stream) {
* // do something with doc
* }
* size_t truncated = stream.truncated_bytes();
*
* IMPORTANT: this value is only meaningful under the conditions below.
*
* - you iterated all the way to the end of the stream;
* - no document reported an error. Iteration stops at the first failed
* document, which can leave the bookkeeping from a mid-stream batch.
*
* If you need to know about a truncated tail outside those conditions, track
* it yourself from the last successful document (see iterator::current_index()
* and iterator::source()).
*
* An empty input (zero bytes) or an input made only of white space contains
* no document: truncated_bytes() returns zero.
*/
inline size_t truncated_bytes() const noexcept;
class iterator {
public:
using value_type = simdjson_result<document>;
using reference = simdjson_result<ondemand::document_reference>;
using pointer = void;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
/**
* Default constructor.
*/
simdjson_inline iterator() noexcept;
simdjson_inline iterator(const iterator &other) noexcept = default;
/**
* Get the current document (or error).
*/
simdjson_inline reference operator*() noexcept;
/**
* Advance to the next document (prefix).
*/
inline iterator& operator++() noexcept;
/**
* Check if we're at the end yet.
* @param other the end iterator to compare to.
*/
simdjson_inline bool operator!=(const iterator &other) const noexcept;
simdjson_inline bool operator==(const iterator &other) const noexcept;
/**
* @private
*
* Gives the current index in the input document in bytes.
*
* document_stream stream = parser.parse_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* auto doc = *i;
* size_t index = i.current_index();
* }
*
* This function (current_index()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*/
simdjson_inline size_t current_index() const noexcept;
/**
* @private
*
* Gives a view of the current document at the current position.
*
* document_stream stream = parser.iterate_many(json,window);
* for(auto i = stream.begin(); i != stream.end(); ++i) {
* std::string_view v = i.source();
* }
*
* The returned string_view instance is simply a map to the (unparsed)
* source string: it may thus include white-space characters and all manner
* of padding. It spans the whole current document, whether or not you
* have already accessed (part of) the document. Thus
* current_index() + source().size() is the offset just past the end of the
* current document, which is useful when reading a stream in chunks.
*
* This function (source()) is experimental and the usage
* may change in future versions of simdjson: we find the API somewhat
* awkward and we would like to offer something friendlier.
*
*/
simdjson_inline std::string_view source() const noexcept;
/**
* Returns error of the stream (if any).
*/
inline error_code error() const noexcept;
/**
* Returns whether the iterator is at the end.
*/
inline bool at_end() const noexcept;
private:
simdjson_inline iterator(document_stream *s, bool finished) noexcept;
/** The document_stream we're iterating through. */
document_stream* stream;
/** Whether we're finished or not. */
bool finished;
friend class document;
friend class document_stream;
friend class json_iterator;
};
using iterator = document_stream::iterator;
/**
* Start iterating the documents in the stream.
*/
simdjson_inline iterator begin() noexcept;
/**
* The end of the stream, for iterator comparison purposes.
*/
simdjson_inline iterator end() noexcept;
private:
document_stream &operator=(const document_stream &) = delete; // Disallow copying
document_stream(const document_stream &other) = delete; // Disallow copying
/**
* Construct a document_stream. Does not allocate or parse anything until the iterator is
* used.
*
* @param parser is a reference to the parser instance used to generate this document_stream
* @param buf is the raw byte buffer we need to process
* @param len is the length of the raw byte buffer in bytes
* @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document)
* @param allow_comma_separated whether to allow comma-separated documents
* @param format the stream format
*/
simdjson_inline document_stream(
ondemand::parser &parser,
const uint8_t *buf,
size_t len,
size_t batch_size,
bool allow_comma_separated,
stream_format format = stream_format::whitespace_delimited
) noexcept;
/**
* Parse the first document in the buffer. Used by begin(), to handle allocation and
* initialization.
*/
inline void start() noexcept;
/**
* Parse the next document found in the buffer previously given to document_stream.
*
* The content should be a valid JSON document encoded as UTF-8. If there is a
* UTF-8 BOM, the parser skips it.
*
* You do NOT need to pre-allocate a parser. This function takes care of
* pre-allocating a capacity defined by the batch_size defined when creating the
* document_stream object.
*
* The function returns simdjson::EMPTY if there is no more data to be parsed.
*
* The function returns simdjson::SUCCESS (as integer = 0) in case of success
* and indicates that the buffer has successfully been parsed to the end.
* Every document it contained has been parsed without error.
*
* The function returns an error code from simdjson/simdjson.h in case of failure
* such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth;
* the simdjson::error_message function converts these error codes into a string).
*
* You can also check validity by calling parser.is_valid(). The same parser can
* and should be reused for the other documents in the buffer.
*/
inline void next() noexcept;
/**
* Move the json_iterator of the document to the location of the next document
* in the stream.
*
* For formats with a document delimiter (`newline_delimited`, `json_sequence`),
* when the iterator is still inside the current document (`depth() > 0`), this
* may jump to the next delimiter instead of walking remaining structurals. That
* jump does not structure-validate the unread remainder.
*/
inline void next_document() noexcept;
/** Byte that ends a document under `format`, or 0 if there is none. */
simdjson_inline uint8_t document_delimiter() const noexcept;
/**
* Position the iterator at the first structural at or past the next
* `delimiter` in the current batch. Returns false if none is found.
*/
simdjson_inline bool skip_to_delimiter(uint8_t delimiter) noexcept;
/** Get the next document index. */
inline size_t next_batch_start() const noexcept;
/** Pass the next batch through stage 1 with the given parser. */
inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept;
// Fields
ondemand::parser *parser;
const uint8_t *buf;
size_t len;
size_t batch_size;
bool allow_comma_separated;
stream_format format;
/**
* We are going to use just one document instance. The document owns
* the json_iterator. It implies that we only ever pass a reference
* to the document to the users.
*/
document doc{};
/** The error (or lack thereof) from the current document. */
error_code error;
size_t batch_start{0};
size_t doc_index{};
#ifdef SIMDJSON_THREADS_ENABLED
/** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */
bool use_thread;
inline void load_from_stage1_thread() noexcept;
/** Start a thread to run stage 1 on the next batch. */
inline void start_stage1_thread() noexcept;
/** Wait for the stage 1 thread to finish and capture the results. */
inline void finish_stage1_thread() noexcept;
/** The error returned from the stage 1 thread. */
error_code stage1_thread_error{UNINITIALIZED};
/** The thread used to run stage 1 against the next batch in the background. */
std::unique_ptr<stage1_worker> worker{};
/**
* The parser used to run stage 1 in the background. Will be swapped
* with the regular parser when finished.
*/
ondemand::parser stage1_thread_parser{};
friend struct stage1_worker;
#endif // SIMDJSON_THREADS_ENABLED
friend class parser;
friend class document;
friend class json_iterator;
friend struct simdjson_result<ondemand::document_stream>;
friend struct simdjson::internal::simdjson_result_base<ondemand::document_stream>;
}; // document_stream
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::document_stream> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::document_stream> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::document_stream &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_H
/* end file simdjson/generic/ondemand/document_stream.h for rvv_vls */
/* including simdjson/generic/ondemand/field.h for rvv_vls: #include "simdjson/generic/ondemand/field.h" */
/* begin file simdjson/generic/ondemand/field.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* A JSON field (key/value pair) in an object.
*
* Returned from object iteration.
*
* Extends from std::pair<raw_json_string, value> so you can use C++ algorithms that rely on pairs.
*/
class field : public std::pair<raw_json_string, value> {
public:
/**
* Create a new invalid field.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline field() noexcept;
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* unescaped_key(): the very same bytes are returned, viewed as char8_t.
*
* This consumes the key: once you have called unescaped_u8key(), you cannot
* call it again nor can you call key().
*/
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a string_view (for higher speed, consider raw_key).
* We deliberately use a more cumbersome name (unescaped_key) to force users
* to think twice about using it. The content is stored in the receiver.
*
* This consumes the key: once you have called unescaped_key(), you cannot
* call it again nor can you call key().
*/
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code unescaped_key(string_type& receiver, bool allow_replacement = false) noexcept;
/**
* Get the key as a raw_json_string. Can be used for direct comparison with
* an unescaped C string: e.g., key() == "test". This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key() for a similar function which returns
* a more convenient std::string_view result.
*/
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. This does not count as
* consumption of the content: you can safely call it repeatedly.
* See escaped_key().
*/
simdjson_inline std::string_view key_raw_json_token() const noexcept;
/**
* Get the key as a string_view. This does not include the quotes and
* the string is unprocessed key so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly. Use unescaped_key() to get the unescaped key.
*/
simdjson_inline std::string_view escaped_key() const noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the key as a C++20 u8string_view. This is a zero-copy alias of
* escaped_key(): the very same bytes are returned, viewed as char8_t.
* The string is unprocessed, so it may contain escape characters
* (e.g., \uXXXX or \n). It does not count as a consumption of the content:
* you can safely call it repeatedly.
*/
simdjson_inline std::u8string_view escaped_u8key() const noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
/**
* Get the field value.
*/
simdjson_inline ondemand::value &value() & noexcept;
/**
* @overload ondemand::value &ondemand::value() & noexcept
*/
simdjson_inline ondemand::value value() && noexcept;
protected:
simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept;
static simdjson_inline simdjson_result<field> start(value_iterator &parent_iter) noexcept;
static simdjson_inline simdjson_result<field> start(const value_iterator &parent_iter, raw_json_string key) noexcept;
friend struct simdjson_result<field>;
friend class object_iterator;
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::field> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::field> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::field &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<std::string_view> unescaped_key(bool allow_replacement = false) noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> unescaped_u8key(bool allow_replacement = false) noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_inline error_code unescaped_key(string_type &receiver, bool allow_replacement = false) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::raw_json_string> key() noexcept;
simdjson_inline simdjson_result<std::string_view> key_raw_json_token() noexcept;
simdjson_inline simdjson_result<std::string_view> escaped_key() noexcept;
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> escaped_u8key() noexcept;
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<rvv_vls::ondemand::value> value() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_H
/* end file simdjson/generic/ondemand/field.h for rvv_vls */
/* including simdjson/generic/ondemand/key_selector.h for rvv_vls: #include "simdjson/generic/ondemand/key_selector.h" */
/* begin file simdjson/generic/ondemand/key_selector.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #include "simdjson/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/common_defs.h" */
/* amalgamation skipped (editor-only): #include "simdjson/constevalutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <array>
#include <string> // std::string (key_selector::describe)
#include <string_view>
#include <cstddef>
#include <cstdint>
#include <cstring> // std::memcpy (portable unaligned window load)
#include <utility> // std::index_sequence (window candidate dispatch)
#include <type_traits> // std::integral_constant (window candidate dispatch)
// AArch64 only: 32-bit ARM (ARMv7) defines __ARM_NEON too, but lacks the
// A64-only horizontal reductions (vmaxvq_u32) used below. See issue #2885.
#if defined(__aarch64__)
#include <arm_neon.h>
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
#endif
#if defined(__SSE2__)
#include <emmintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
#endif
#if defined(__loongarch_sx)
#include <lsxintrin.h>
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
#else
#define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
#endif
// Clang 15 and earlier reject a call to a consteval function from within a
// consteval function template when an argument refers to one of the caller's
// reference parameters ("function parameter 'keys' with unknown value cannot
// be used in a constant expression"). With those compilers, the helpers below
// are merely constexpr: the perfect-hash tables are still computed at compile
// time because they initialize static constexpr data members.
#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL
#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16))
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr
#else
#define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval
#endif
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
namespace key_selector_detail {
// Since not constexpr, triggers compile-time error.
inline void compile_time_error(const char* message) noexcept { (void)message; }
// ============================================================================
// Compile-time perfect-hash generator.
// It scales to ~100 keys at compile time by determining association values one (position, character)
// symbol at a time (gperf-style) instead of an exhaustive offset search, and
// falls back to a Hash-and-Displace construction for large/awkward key sets.
//
// Only flat tables survive to runtime; the lookup is a few additions plus a
// single SIMD key comparison (see match_raw below).
// ============================================================================
// Maximum number of character positions the gperf hash may combine.
static constexpr std::size_t MAX_POSITIONS = 16;
// Sentinel "position" meaning "the last character of the key".
static constexpr std::size_t LAST_CHAR = std::size_t(-1);
// Runtime-encoded sentinels (stored in uint8 tables).
static constexpr std::uint8_t POS_LAST_CHAR = 0xFF; // positions_[i] == last char
static constexpr std::uint8_t HD_MODE = 0xFF; // num_positions == H&D mode
// Flags stored in positions[2] in H&D mode to select the key-hash variant.
static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
constexpr std::size_t next_power_of_2(std::size_t n) noexcept {
if (n == 0) { return 1; }
std::size_t p = 1;
while (p < n) { p <<= 1; }
return p;
}
// Character at a given position (LAST_CHAR means last character), or 256 if out
// of bounds.
constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept {
if (pos == LAST_CHAR) {
if (key.empty()) { return 256; }
return static_cast<unsigned char>(key[key.size() - 1]);
}
if (pos >= key.size()) { return 256; }
return static_cast<unsigned char>(key[pos]);
}
// Count key pairs that a set of positions fails to distinguish. Keys whose
// lengths differ modulo the table size are separated by the length term in the
// hash, so they need no position coverage.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i].size() % modulus != keys[j].size() % modulus) { continue; }
bool distinguished = false;
for (std::size_t p = 0; p < num_positions; ++p) {
if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
distinguished = true;
break;
}
}
if (!distinguished) { ++count; }
}
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish(
const std::array<std::string_view, N>& keys,
const std::size_t* positions,
std::size_t num_positions,
std::size_t modulus) {
return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
}
// Number of distinct (length % modulus, char_at(key, pos)) pairs at a position.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power(
const std::array<std::string_view, N>& keys,
std::size_t pos,
std::size_t modulus) {
struct pair { std::size_t len_mod; std::size_t ch; };
std::array<pair, N> pairs{};
for (std::size_t i = 0; i < N; ++i) {
pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
}
std::size_t count = 0;
for (std::size_t i = 0; i < N; ++i) {
bool dup = false;
for (std::size_t j = 0; j < i; ++j) {
if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
dup = true;
break;
}
}
if (!dup) { ++count; }
}
return count;
}
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array<std::string_view, N>& keys) {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); }
}
return m;
}
// Bounded backtracking DFS for a minimal set of distinguishing positions.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search(
const std::array<std::string_view, N>& keys,
const std::size_t* candidates,
std::size_t num_candidates,
std::size_t* positions,
std::size_t& num_positions_out,
std::size_t& budget,
std::size_t modulus) {
constexpr std::size_t MAX_DEPTH = 8;
std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
std::array<frame, MAX_DEPTH + 1> stack{};
std::size_t sp = 0;
std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
if (budget > 0) { --budget; }
if (initial_count == 0) { num_positions_out = 0; return true; }
stack[0] = {0, 0, initial_count};
while (budget > 0) {
if (sp > MAX_DEPTH) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
auto& f = stack[sp];
if (f.next_ci >= breadth) {
if (sp == 0) { break; }
--sp;
++stack[sp].next_ci;
continue;
}
positions[sp] = candidates[f.next_ci];
--budget;
std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
if (new_count == 0) { num_positions_out = sp + 1; return true; }
if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
++sp;
} else {
++f.next_ci;
}
}
return false;
}
// Phase 1: select character positions that distinguish all colliding pairs.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions(
const std::array<std::string_view, N>& keys,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::size_t modulus) {
if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) { return 0; }
std::size_t max_len = max_key_length(keys);
constexpr std::size_t MAX_CANDIDATES = 256;
std::array<std::size_t, MAX_CANDIDATES> candidates{};
std::array<std::size_t, MAX_CANDIDATES> powers{};
std::size_t num_candidates = 0;
for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
candidates[num_candidates] = p;
powers[num_candidates] = discriminating_power(keys, p, modulus);
++num_candidates;
}
if (num_candidates < MAX_CANDIDATES) {
candidates[num_candidates] = LAST_CHAR;
powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
++num_candidates;
}
for (std::size_t i = 0; i < num_candidates; ++i) {
for (std::size_t j = i + 1; j < num_candidates; ++j) {
if (powers[j] > powers[i]) {
auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
}
}
}
positions[0] = candidates[0];
if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) { return 1; }
positions[0] = 0;
positions[1] = LAST_CHAR;
if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) { return 2; }
{
std::size_t budget = 5000;
std::size_t num_found = 0;
if (backtracking_search<N>(keys, candidates.data(), num_candidates,
positions.data(), num_found, budget, modulus)) {
return num_found;
}
}
std::size_t num_pos = 0;
for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
bool already = false;
for (std::size_t p = 0; p < num_pos; ++p) {
if (positions[p] == candidates[ci]) { already = true; break; }
}
if (already) { continue; }
positions[num_pos] = candidates[ci];
++num_pos;
if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) { return num_pos; }
}
compile_time_error("Failed to find distinguishing positions for perfect hash");
return 0;
}
// Result of PHF computation. A max-sized slot_to_key array lets the same struct
// type carry any chosen table size.
template <std::size_t N>
struct phf_result {
// Allow up to 8x the minimum table size. Sparser tables solve faster.
static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
std::size_t table_size{};
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
std::size_t num_positions{};
std::array<std::size_t, MAX_POSITIONS> positions{};
std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
};
// Partition-based asso_values search (gperf-style). Determines asso_values one
// (position, character) symbol at a time; never revisits a value. Equivalence
// classes (keys sharing the same undetermined symbols) keep the search cheap.
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
}
if (num_positions == 0) {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
for (std::size_t k = 0; k < N; ++k) {
for (std::size_t p = 0; p < num_positions; ++p) {
kchars[k][p] = char_at(keys[k], positions[p]);
}
}
struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
std::array<sym_t, MAX_SYMS> syms{};
std::size_t nsyms = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::array<std::size_t, 256> freq{};
for (std::size_t k = 0; k < N; ++k) {
std::size_t c = kchars[k][p];
if (c < 256) { freq[c]++; }
}
for (std::size_t c = 0; c < 256; ++c) {
if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
}
}
for (std::size_t i = 0; i < nsyms; ++i) {
for (std::size_t j = i + 1; j < nsyms; ++j) {
if (syms[j].freq > syms[i].freq) {
auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
}
}
}
std::array<std::size_t, N> phash{};
for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
{
uint64_t s = 0x9e3779b97f4a7c15ULL;
for (std::size_t p = 0; p < num_positions; ++p) {
for (std::size_t c = 0; c < 256; ++c) {
s = s * 6364136223846793005ULL + 1442695040888963407ULL;
salt[p][c] = s;
}
}
}
std::array<uint64_t, N> sig{};
for (std::size_t k = 0; k < N; ++k) {
uint64_t s = 0;
for (std::size_t p = 0; p < num_positions; ++p) {
std::size_t c = kchars[k][p];
if (c < 256) { s ^= salt[p][c]; }
}
sig[k] = s;
}
std::array<std::size_t, N> order{};
for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
std::array<std::size_t, M> slot_gen{};
std::size_t gen = 0;
std::size_t search_limit = next_power_of_2(M);
if (search_limit < 32) { search_limit = 32; }
for (std::size_t si = 0; si < nsyms; ++si) {
std::size_t sp = syms[si].pos;
std::size_t sc = syms[si].ch;
uint64_t sp_salt = salt[sp][sc];
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
}
for (std::size_t i = 1; i < N; ++i) {
std::size_t x = order[i];
uint64_t xs = sig[x];
std::size_t j = i;
while (j > 0 && sig[order[j - 1]] > xs) {
order[j] = order[j - 1];
--j;
}
order[j] = x;
}
bool found = false;
for (std::size_t v = 0; v < search_limit && !found; ++v) {
bool collision = false;
std::size_t ci = 0;
while (ci < N && !collision) {
uint64_t class_sig = sig[order[ci]];
std::size_t cj = ci;
while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
if (cj - ci > 1) {
++gen;
for (std::size_t x = ci; x < cj; ++x) {
std::size_t k = order[x];
std::size_t h = phash[k];
if (kchars[k][sp] == sc) { h += v; }
h %= M;
if (slot_gen[h] == gen) { collision = true; break; }
slot_gen[h] = gen;
}
}
ci = cj;
}
if (!collision) {
asso_values[sp][sc] = v;
for (std::size_t k = 0; k < N; ++k) {
if (kchars[k][sp] == sc) { phash[k] += v; }
}
found = true;
}
}
if (!found) { return false; }
}
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = phash[i] % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M, std::size_t MaxM>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
if (try_compute_phf<N, M>(keys, result)) { return true; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= MaxM) { return try_gperf_po2<N, NextM, MaxM>(keys, result); }
return false;
}
// --- Hash-and-Displace fallback --------------------------------------------
constexpr std::size_t hd_bucket_hash(std::string_view key) noexcept {
std::size_t c0 = key.empty() ? 0 : static_cast<unsigned char>(key[0]);
std::size_t c1 = key.empty() ? 0 : static_cast<unsigned char>(key[key.size() - 1]);
return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
}
constexpr std::size_t hd_safe_char(const char* p, std::size_t len, std::size_t idx) noexcept {
std::size_t has = static_cast<std::size_t>(idx < len);
std::size_t si = idx & (std::size_t{0} - has);
return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
}
constexpr std::size_t hd_key_hash_2(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
return kc;
}
constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept {
std::size_t kc = key.size();
kc = kc * 31 + static_cast<unsigned char>(key[0]);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
return kc;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace(
const std::array<std::string_view, N>& keys,
std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
std::size_t& num_positions,
std::array<std::size_t, MAX_POSITIONS>& positions,
std::array<std::size_t, M>& slot_to_key) {
num_positions = select_positions<N>(keys, positions, M);
if (num_positions == 0) {
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < N; ++i) {
std::size_t slot = keys[i].size() % M;
if (slot_to_key[slot] != N) { return false; }
slot_to_key[slot] = i;
}
return true;
}
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
num_positions = HD_MODE; // sentinel for H&D mode
positions[0] = 0;
positions[1] = LAST_CHAR;
std::array<std::size_t, N> key_bucket{};
for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
struct bucket_info { std::size_t ch; std::size_t count; };
std::array<bucket_info, N> buckets{};
std::size_t num_buckets = 0;
for (std::size_t i = 0; i < N; ++i) {
std::size_t bk = key_bucket[i];
bool found = false;
for (std::size_t b = 0; b < num_buckets; ++b) {
if (buckets[b].ch == bk) { ++buckets[b].count; found = true; break; }
}
if (!found) { buckets[num_buckets++] = {bk, 1}; }
}
for (std::size_t i = 0; i < num_buckets; ++i) {
for (std::size_t j = i + 1; j < num_buckets; ++j) {
if (buckets[j].count > buckets[i].count) {
auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
}
}
}
auto try_placement = [&](auto key_hash_fn) -> bool {
for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
for (std::size_t b = 0; b < num_buckets; ++b) {
std::size_t ch = buckets[b].ch;
std::array<std::size_t, N> bucket_keys{};
std::size_t bk_count = 0;
for (std::size_t i = 0; i < N; ++i) {
if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
}
bool placed = false;
std::size_t max_d = M < 255 ? M : 255;
for (std::size_t d = 0; d < max_d; ++d) {
bool ok = true;
std::array<std::size_t, N> bucket_slots{};
for (std::size_t k = 0; k < bk_count; ++k) {
std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
if (slot_to_key[slot] != N) { ok = false; break; }
for (std::size_t k2 = 0; k2 < k; ++k2) {
if (bucket_slots[k2] == slot) { ok = false; break; }
}
if (!ok) { break; }
bucket_slots[k] = slot;
}
if (ok) {
asso_values[0][ch] = d;
for (std::size_t k = 0; k < bk_count; ++k) {
slot_to_key[bucket_slots[k]] = bucket_keys[k];
}
placed = true;
break;
}
}
if (!placed) { return false; }
}
std::size_t filled = 0;
for (std::size_t i = 0; i < M; ++i) {
if (slot_to_key[i] != N) { ++filled; }
}
return filled == N;
};
if (try_placement([](std::string_view k) { return hd_key_hash_2(k); })) {
positions[2] = HD_HASH_2BYTE_FLAG;
return true;
}
if (try_placement([](std::string_view k) { return hd_key_hash_4(k); })) {
positions[2] = HD_HASH_4BYTE_FLAG;
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array<std::string_view, N>& keys, phf_result<N>& result) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
std::size_t npos{};
std::array<std::size_t, MAX_POSITIONS> pos{};
std::array<std::size_t, M> s2k{};
if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
result.table_size = M;
result.asso_values = asso;
result.num_positions = npos;
result.positions = pos;
for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
return true;
}
return false;
}
template <std::size_t N, std::size_t M>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf_hd_po2(const std::array<std::string_view, N>& keys) {
static_assert(M <= phf_result<N>::MAX_TABLE_SIZE, "Table size M exceeds maximum");
phf_result<N> result{};
if (try_compute_phf_hd<N, M>(keys, result)) { return result; }
constexpr std::size_t NextM = M * 2;
if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
return compute_phf_hd_po2<N, NextM>(keys);
} else {
compile_time_error("Hash-and-Displace: failed to find valid table size");
return result;
}
}
// Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so
// the runtime tables stay within uint8 indices), then fall back to H&D.
template <std::size_t N>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result<N> compute_phf(const std::array<std::string_view, N>& keys) {
constexpr std::size_t StartM = next_power_of_2(N);
constexpr std::size_t GPERF_MAX_TABLE =
phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
if constexpr (StartM <= GPERF_MAX_TABLE) {
phf_result<N> result{};
if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) { return result; }
}
return compute_phf_hd_po2<N, StartM>(keys);
}
// ============================================================================
// Runtime tables (flat, uint8) derived from a phf_result.
// ============================================================================
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
struct phf_data {
std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
std::array<std::uint8_t, MAX_POSITIONS> positions{};
std::uint8_t num_positions{};
std::uint8_t hd_hash_variant{}; // 2 or 4 (H&D only)
std::array<std::uint8_t, TableSize> slot_to_key{};
// slot_key_bytes[s] holds the key stored at slot s, zero-padded to a 16-byte
// multiple so the SIMD comparison can read a whole register.
std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
std::array<std::uint8_t, TableSize> slot_key_len{};
};
template <std::size_t N>
constexpr std::size_t compute_max_key_len(const std::array<std::string_view, N>& keys) noexcept {
std::size_t m = 0;
for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } }
return m;
}
// Validate keys and build the runtime tables from the computed perfect hash.
template <std::size_t N, std::size_t TableSize, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data<N, TableSize, MaxKeyLen>
build_phf_data(const std::array<std::string_view, N>& keys, const phf_result<N>& result) {
for (std::size_t i = 0; i < N; ++i) {
if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); }
if (keys[i].size() > MaxKeyLen) { compile_time_error("key length exceeds MaxKeyLen"); }
for (char c : keys[i]) {
if (c == '\\') { compile_time_error("backslash not allowed in key_selector keys"); }
if (c == '"') { compile_time_error("quote not allowed in key_selector keys"); }
if (c == '\0') { compile_time_error("null byte not allowed in key_selector keys"); }
}
for (std::size_t j = i + 1; j < N; ++j) {
if (keys[i] == keys[j]) { compile_time_error("duplicate keys in key_selector"); }
}
}
phf_data<N, TableSize, MaxKeyLen> out{};
if (result.num_positions == HD_MODE) {
// H&D mode: single displacement table in asso_values[0].
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[0][c] = static_cast<std::uint8_t>(result.asso_values[0][c]);
}
out.num_positions = static_cast<std::uint8_t>(HD_MODE);
out.hd_hash_variant = static_cast<std::uint8_t>(result.positions[2]);
} else {
for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
for (std::size_t c = 0; c < 256; ++c) {
out.asso_values[pi][c] = static_cast<std::uint8_t>(result.asso_values[pi][c] % TableSize);
}
}
out.num_positions = static_cast<std::uint8_t>(result.num_positions);
for (std::size_t i = 0; i < result.num_positions; ++i) {
out.positions[i] = (result.positions[i] == LAST_CHAR)
? POS_LAST_CHAR
: static_cast<std::uint8_t>(result.positions[i]);
}
}
for (std::size_t s = 0; s < TableSize; ++s) {
out.slot_to_key[s] = static_cast<std::uint8_t>(result.slot_to_key[s]);
}
for (std::size_t s = 0; s < TableSize; ++s) {
std::size_t ki = result.slot_to_key[s];
if (ki < N) {
auto k = keys[ki];
out.slot_key_len[s] = static_cast<std::uint8_t>(k.size());
for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
} else {
out.slot_key_len[s] = 0; // empty slot: no length can match
}
}
return out;
}
// --- SIMD runtime primitives ------------------------------------------------
// The runtime matchers read whole 16-byte blocks up to offset 63 (four blocks)
// for keys as long as the 63-character maximum. That read must stay within the
// buffer's trailing padding, so the padding has to exceed the largest offset we
// touch.
static_assert(SIMDJSON_PADDING > 63,
"key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
// True when every byte of `diff` is zero. A 32-bit-lane horizontal max is
// enough (zero iff every 32-bit word is zero) and is cheaper than a byte-wide
// reduction. Do not replace this with a floating-point compare against 0.0:
// flush-to-zero would treat a denormal as zero.
simdjson_really_inline bool neon_all_bytes_zero(uint8x16_t diff) noexcept {
return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
}
#endif
// Scan for the terminating '"' starting at p. Returns its byte offset (= key
// length). Caller guarantees SIMDJSON_PADDING (== 64) bytes past the JSON buffer,
// so reading whole 16-byte blocks up to offset 63 is always safe.
template <std::size_t MaxKeyLen>
simdjson_really_inline std::size_t scan_key_length(const char* p) noexcept {
// The closing quote of the longest key sits at most at offset MaxKeyLen, so we
// scan as many 16-byte blocks as it takes to cover offsets 0..MaxKeyLen. The
// cap (63) keeps every covered offset within the 64-byte padding guarantee, so
// the SIMD and scalar builds agree.
static_assert(MaxKeyLen <= 63, "MaxKeyLen must be <= 63 for current SIMD implementations");
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t v = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8('"'));
uint64_t m = vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
if (simdjson_likely(m != 0)) { return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8('"'));
unsigned m = static_cast<unsigned>(_mm_movemask_epi8(cmp));
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i v = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b('"'));
// vmskltz_b gathers the per-byte sign bits (set where the byte equals '"')
// into the low 16 bits of lane 0, the LSX equivalent of movemask.
unsigned m = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
if (simdjson_likely(m != 0)) { return b * 16 + std::size_t(__builtin_ctz(m)); }
}
return MaxKeyLen + 1;
#else
for (std::size_t i = 0; i <= MaxKeyLen; ++i)
if (p[i] == '"') return i;
return MaxKeyLen + 1;
#endif
}
// Byte-equal of p[0..len) against stored[0..len). stored is zero-padded past
// `len`. Input is read over 16, 32, 48, or 64 bytes (padded JSON buffer
// guaranteed; SIMDJSON_PADDING == 64).
template <std::size_t MaxKeyLen>
simdjson_really_inline bool compare_key_bytes(
const char* p, const char* stored, std::size_t len) noexcept {
// [[maybe_unused]]: only the NEON/SSE2/LSX branches read these; the scalar
// fallback build (no SIMD) leaves them unused, which is an error under -Werror.
[[maybe_unused]] alignas(16) static constexpr uint8_t idx16[16] =
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
if constexpr (MaxKeyLen <= 16) {
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(static_cast<uint8_t>(len)));
uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
return neon_all_bytes_zero(diff);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i idx = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(static_cast<char>(len)));
__m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
return _mm_movemask_epi8(eq) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i idx = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(static_cast<int>(len)));
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
return (static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 32) {
[[maybe_unused]] alignas(16) static constexpr uint8_t idx32_hi[16] =
{16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t vp_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(p));
uint8x16_t vp_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + 16);
uint8x16_t vs_lo = vld1q_u8(reinterpret_cast<const uint8_t*>(stored));
uint8x16_t vs_hi = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + 16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i vp_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
__m128i vp_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + 16));
__m128i vs_lo = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored));
__m128i vs_hi = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + 16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
__m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx16)), lenv);
__m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(idx32_hi)), lenv);
__m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
__m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
__m128i vp_lo = __lsx_vld(reinterpret_cast<const void*>(p), 0);
__m128i vp_hi = __lsx_vld(reinterpret_cast<const void*>(p + 16), 0);
__m128i vs_lo = __lsx_vld(reinterpret_cast<const void*>(stored), 0);
__m128i vs_hi = __lsx_vld(reinterpret_cast<const void*>(stored + 16), 0);
__m128i m_lo = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx16), 0), lenv);
__m128i m_hi = __lsx_vslt_b(__lsx_vld(reinterpret_cast<const void*>(idx32_hi), 0), lenv);
__m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
__m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
unsigned mlo = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
unsigned mhi = static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
return (mlo & mhi) == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else if constexpr (MaxKeyLen <= 64) {
// 3 or 4 16-byte blocks (33..48 -> 3, 49..64 -> 4). stored is zero-padded
// to exactly num_blocks*16 bytes (KEY_STRIDE), so neither load overruns it.
// [[maybe_unused]]: the scalar fallback (no NEON/SSE2/LSX) does not use it.
[[maybe_unused]] constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
#if SIMDJSON_KEY_SELECTOR_HAS_NEON
uint8x16_t base = vld1q_u8(idx16);
uint8x16_t lenv = vdupq_n_u8(static_cast<uint8_t>(len));
uint8x16_t acc = vdupq_n_u8(0);
for (std::size_t b = 0; b < num_blocks; ++b) {
uint8x16_t vp = vld1q_u8(reinterpret_cast<const uint8_t*>(p) + b * 16);
uint8x16_t vs = vld1q_u8(reinterpret_cast<const uint8_t*>(stored) + b * 16);
uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(static_cast<uint8_t>(b * 16)));
uint8x16_t mask = vcltq_u8(idxv, lenv);
acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
}
return neon_all_bytes_zero(acc);
#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
__m128i base = _mm_load_si128(reinterpret_cast<const __m128i*>(idx16));
__m128i lenv = _mm_set1_epi8(static_cast<char>(len));
int eq = 0xFFFF;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = _mm_loadu_si128(reinterpret_cast<const __m128i*>(p + b * 16));
__m128i vs = _mm_loadu_si128(reinterpret_cast<const __m128i*>(stored + b * 16));
__m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(static_cast<char>(b * 16)));
__m128i mask = _mm_cmplt_epi8(idxv, lenv);
eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
}
return eq == 0xFFFF;
#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
__m128i base = __lsx_vld(reinterpret_cast<const void*>(idx16), 0);
__m128i lenv = __lsx_vreplgr2vr_b(static_cast<int>(len));
unsigned acc = 0xFFFFu;
for (std::size_t b = 0; b < num_blocks; ++b) {
__m128i vp = __lsx_vld(reinterpret_cast<const void*>(p + b * 16), 0);
__m128i vs = __lsx_vld(reinterpret_cast<const void*>(stored + b * 16), 0);
__m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(static_cast<int>(b * 16)));
__m128i mask = __lsx_vslt_b(idxv, lenv);
__m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
acc &= static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
}
return acc == 0xFFFFu;
#else
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
#endif
} else {
for (std::size_t i = 0; i < len; ++i)
if (p[i] != stored[i]) return false;
return true;
}
}
// --- Single 8-bit window fast path ------------------------------------------
//
// Many small key sets can be told apart by inspecting a *single* 8-bit window of
// the key bytes -- and that window need not be byte-aligned. Because every JSON
// key is terminated by a '"', the bytes at and before a key's length are well
// defined for any key at least that long: byte i is the key character when i is
// inside the key and the closing quote when i == len. So we read two bytes at a
// fixed offset, extract 8 consecutive bits at a fixed intra-byte shift, and if
// that value is distinct for every key, a 256-entry table maps it straight to a
// candidate key. The match then needs no hash and -- in the length-free overload
// -- no SIMD length scan: load two bytes, shift, mask, index the table, and
// confirm the candidate with one comparison.
//
// Allowing an *unaligned* window (a shift of 1..7) mixes bits from two adjacent
// bytes and discriminates key sets that no single aligned byte can. For example,
// the partial_tweets keys {created_at,id,text,in_reply_to_status_id,user,
// retweet_count,favorite_count} share a colliding byte at every aligned position
// 0,1,2, yet the 8 bits starting at bit offset 2 are unique across all seven.
// Simpler cases fall out as the shift==0 special case: {"id","screen_name"}
// splits on byte 0, {"jo","joe"} on the quote at byte 2.
//
// The window is confined to the first (shortest key length + 1) bytes so the
// two-byte read never crosses a key's closing quote into uncontrolled value
// bytes; that final byte is the shortest key's quote.
template <std::size_t N, std::size_t MaxKeyLen>
struct window_data {
static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
bool ok{false};
std::uint8_t byte_offset{0}; // first byte of the 2-byte read
std::uint8_t shift{0}; // intra-byte bit shift (0..7)
std::array<std::uint8_t, 256> window_to_key{}; // window byte -> key index, N if none
std::array<std::uint8_t, N> key_len{};
std::array<std::array<char, KEY_STRIDE>, N> key_bytes{}; // zero-padded
};
// Byte seen at `idx` for key `i`: a key character when idx is inside the key, or
// the closing '"' when idx == len (callers keep idx <= every key's length).
template <std::size_t N>
constexpr unsigned window_byte_at(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t idx) noexcept {
if (idx < keys[i].size()) { return static_cast<unsigned char>(keys[i][idx]); }
return static_cast<unsigned>('"');
}
// The 8-bit window value for key `i` at (byte_offset, shift): the little-endian
// pair (byte[off], byte[off+1]) shifted right by `shift` and truncated. Matches
// the runtime read exactly.
template <std::size_t N>
constexpr unsigned window_value(const std::array<std::string_view, N>& keys,
std::size_t i, std::size_t byte_offset, std::size_t shift) noexcept {
unsigned lo = window_byte_at<N>(keys, i, byte_offset);
unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
return ((lo | (hi << 8)) >> shift) & 0xFFu;
}
template <std::size_t N, std::size_t MaxKeyLen>
SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data<N, MaxKeyLen>
compute_window(const std::array<std::string_view, N>& keys) {
window_data<N, MaxKeyLen> out{};
std::size_t min_len = keys[0].size();
for (std::size_t i = 1; i < N; ++i) {
if (keys[i].size() < min_len) { min_len = keys[i].size(); }
}
// Iterate windows nearest the front first (cheapest to read, smallest shift).
for (std::size_t off = 0; off <= min_len; ++off) {
for (std::size_t shift = 0; shift < 8; ++shift) {
// The read touches byte off, and byte off+1 when shift != 0. Both must
// stay within the safe region [0, min_len] (min_len is the shortest
// key's quote index). off <= min_len is guaranteed by the loop bound.
if (shift != 0 && off + 1 > min_len) { continue; }
bool distinct = true;
for (std::size_t i = 0; i < N && distinct; ++i) {
for (std::size_t j = i + 1; j < N; ++j) {
if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
distinct = false;
break;
}
}
}
if (!distinct) { continue; }
out.ok = true;
out.byte_offset = static_cast<std::uint8_t>(off);
out.shift = static_cast<std::uint8_t>(shift);
for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] = static_cast<std::uint8_t>(N); }
for (std::size_t i = 0; i < N; ++i) {
out.window_to_key[window_value<N>(keys, i, off, shift)] = static_cast<std::uint8_t>(i);
out.key_len[i] = static_cast<std::uint8_t>(keys[i].size());
for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
}
return out;
}
}
return out; // ok == false: no single 8-bit window distinguishes the keys
}
// Read the 8-bit window at (byte_offset, shift) from a padded key pointer.
simdjson_really_inline std::uint8_t read_window(const char* p, std::size_t byte_offset,
std::size_t shift) noexcept {
std::uint16_t w;
// Two controlled bytes (within the shortest key + its quote, hence within the
// padded buffer). memcpy is the portable little-endian unaligned load.
std::memcpy(&w, p + byte_offset, sizeof(w));
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
w = static_cast<std::uint16_t>((w >> 8) | (w << 8));
#endif
return static_cast<std::uint8_t>((w >> shift) & 0xFFu);
}
// Verify a window candidate. The window table already mapped the key to the
// single possible index `ki` (< N); here we confirm it. Folding over 0..N-1
// turns the runtime `ki` into a compile-time index in the matching arm, so the
// candidate's length and bytes are constants for compare_key_bytes -- the same
// specialization the ordered find_field path gets from a CT-length
// unsafe_is_equal, and what keeps this path competitive. The closing-quote check
// (p[L] == '"') both confirms the key ends exactly at the candidate's length and
// rejects a wrong-length key, so this works whether or not the caller knew len.
template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
simdjson_really_inline std::size_t
match_window_candidate(const char* p, std::uint8_t ki,
const window_data<N, MaxKeyLen>& w,
std::index_sequence<Is...>) noexcept {
std::size_t result = N;
auto try_match = [&](auto Ic) {
constexpr std::size_t i = decltype(Ic)::value;
if (ki == i && p[w.key_len[i]] == '"' &&
key_selector_detail::compare_key_bytes<MaxKeyLen>(
p, w.key_bytes[i].data(), w.key_len[i])) {
result = i;
}
};
(try_match(std::integral_constant<std::size_t, Is>{}), ...);
return result;
}
// --- describe() string helpers (constexpr; no std::to_string, which is not) ---
// Append the decimal form of v to s.
inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) {
if (v == 0) { s.push_back('0'); return; }
char buf[20];
std::size_t n = 0;
while (v > 0) { buf[n++] = static_cast<char>('0' + (v % 10)); v /= 10; }
while (n > 0) { s.push_back(buf[--n]); }
}
// Append a byte as its decimal value, plus the printable character in quotes
// when it is in the printable ASCII range (e.g. "110 ('n')").
inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) {
append_uint(s, b);
if (b >= 0x20 && b < 0x7f) {
s += " ('";
s.push_back(static_cast<char>(b));
s += "')";
}
}
} // namespace key_selector_detail
/**
* Stateless, compile-time key selector.
*
* Usage:
* using sel_t = key_selector<"id", "text", "user">;
* std::size_t i = sel_t::match_raw(raw_key); // returns sel_t::size() on miss
*
* The perfect hash is built at compile time (gperf-style, with a
* Hash-and-Displace fallback) and only flat tables survive to runtime. All
* tables are static constexpr, so the lookup fully inlines.
*
* Limitations:
* - Each key must be at most 63 characters long (and no longer than
* SIMDJSON_PADDING). Longer keys trigger a compile-time error.
* - The number of keys should be moderate. The hard limit is 255 keys;
* compilation time grows with the number of keys, so prefer a few dozen at
* most per selector.
* - Keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes (matching is done against the raw, unescaped JSON key bytes).
*/
template <constevalutil::fixed_string... Keys>
struct key_selector {
static constexpr std::size_t N = sizeof...(Keys);
static_assert(N > 0, "key_selector requires at least one key");
static_assert(N <= 255,"key_selector supports at most 255 keys");
static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
static_assert(max_key_len <= SIMDJSON_PADDING,
"key longer than SIMDJSON_PADDING is not supported");
// The SIMD key-length scan covers offsets 0..63 (four 16-byte blocks), which
// stays within the 64-byte padding guarantee. A 64-character key's closing
// quote would land at offset 64 and be missed on NEON/SSE2/LSX while still
// matching in scalar builds, so cap at 63 to keep implementations in agreement.
static_assert(max_key_len <= 63,
"key_selector keys must be at most 63 characters long");
static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
static constexpr std::size_t table_size = result.table_size;
static constexpr auto phf =
key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
// Single 8-bit-window discriminator (when one exists). Detected at compile
// time and selected with `if constexpr` below, so the hash path is compiled
// out for key sets that qualify, and this is compiled out for those that do
// not.
static constexpr auto window =
key_selector_detail::compute_window<N, max_key_len>(keys);
static constexpr std::size_t size() noexcept { return N; }
/**
* Look up a JSON key whose length is already known. p must point at the first
* key byte (just after the opening quote) in a padded simdjson buffer, and len
* must be the number of raw key bytes (the distance to the closing quote).
* Returns the selector index in [0, N) on match, or N on miss.
*
* Prefer this overload when the caller can obtain the key length cheaply (for
* example, object::for_each derives it from the structural index rather than
* re-scanning for the closing quote).
*/
static simdjson_really_inline std::size_t match_raw(const char* p, std::size_t len) noexcept {
if (len == 0 || len > max_key_len) { return N; }
if constexpr (window.ok) {
// One 8-bit window selects the only possible candidate key;
// match_window_candidate confirms it (bytes + closing quote). p sits
// in a padded buffer and the window stays within the shortest key +
// quote, so the two-byte read is always in bounds. len is unused here
// because the quote check already pins the key's end.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
std::size_t slot;
if (phf.num_positions == key_selector_detail::HD_MODE) {
// Hash-and-Displace: bucket displacement + per-key hash.
std::string_view key(p, len);
std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(key)
: key_selector_detail::hd_key_hash_4(key);
slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
} else {
// gperf: h = len + sum of asso_values over the selected positions.
// positions / num_positions / asso_values are compile-time constants,
// so this loop fully unrolls. The idx < len guard mirrors the
// generator's char_at()-> 256 -> skip behavior for out-of-range
// positions (required: arbitrary positions may exceed a key's length).
std::size_t h = len;
for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
std::uint8_t pos = phf.positions[i];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (len - std::size_t{1})
: static_cast<std::size_t>(pos);
if (idx < len) {
h += phf.asso_values[i][static_cast<unsigned char>(p[idx])];
}
}
slot = h & (table_size - 1);
}
std::uint8_t ki = phf.slot_to_key[slot];
if (ki >= N) { return N; }
if (phf.slot_key_len[slot] != len) { return N; }
if (!key_selector_detail::compare_key_bytes<max_key_len>(
p, phf.slot_key_bytes[slot].data(), len)) { return N; }
return ki;
}
/**
* Look up a JSON key. rjs must point just after an opening quote in a padded
* simdjson buffer. Returns the selector index in [0, N) on match, or N on miss.
* The key length is recovered with a SIMD scan for the closing quote; callers
* that already know the length should use the (p, len) overload above.
*/
static simdjson_really_inline std::size_t match_raw(raw_json_string rjs) noexcept {
const char* p = rjs.raw();
if constexpr (window.ok) {
// One 8-bit window picks the candidate; verifying the candidate's
// bytes and its closing '"' confirms the full key, so the length scan
// is unnecessary. The window read is in bounds (padding), and the
// candidate length is at most max_key_len.
std::uint8_t ki = window.window_to_key[
key_selector_detail::read_window(p, window.byte_offset, window.shift)];
if (ki >= N) { return N; }
return key_selector_detail::match_window_candidate(
p, ki, window, std::make_index_sequence<N>{});
}
return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
}
/** Return the key text at selector index i (i in [0, N)). */
static constexpr std::string_view key_at(std::size_t i) noexcept {
return keys[i];
}
/**
* Return a complete, human-readable, multi-line description of how this
* selector classifies a key: which algorithm was selected at compile time
* (single 8-bit window, gperf-style perfect hash, or hash-and-displace), the
* exact bytes/positions it inspects, and the contents of the lookup tables
* (which window bytes or hash slots map to which key). The text mirrors what
* match_raw() does step by step.
*
* Everything it reports is derived from the compile-time tables, so describe()
* is itself usable in a constant expression when the standard library supports
* constexpr std::string (__cpp_lib_constexpr_string):
*
* static_assert(!key_selector<"name", "city">::describe().empty());
*
* It allocates a std::string and is meant for documentation, debugging and
* tests, not for any hot path.
*/
static SIMDJSON_CONSTEXPR_STRING std::string describe() {
std::string s;
s += "key_selector: ";
key_selector_detail::append_uint(s, N);
s += " keys, max key length ";
key_selector_detail::append_uint(s, max_key_len);
s += "\nkeys:\n";
for (std::size_t i = 0; i < N; ++i) {
s += " [";
key_selector_detail::append_uint(s, i);
s += "] \"";
s += keys[i];
s += "\" (length ";
key_selector_detail::append_uint(s, keys[i].size());
s += ")\n";
}
if constexpr (window.ok) {
// Mirrors the window fast path of match_raw().
s += "algorithm: single 8-bit window\n";
s += " step 1: read 2 bytes at offset ";
key_selector_detail::append_uint(s, window.byte_offset);
s += ", interpret them as a little-endian 16-bit value, shift right by ";
key_selector_detail::append_uint(s, window.shift);
s += " bits, and keep the low 8 bits\n";
s += " step 2: map that byte through a 256-entry table to a key index (";
key_selector_detail::append_uint(s, N);
s += " means no match):\n";
for (std::size_t b = 0; b < 256; ++b) {
if (window.window_to_key[b] < N) {
s += " byte ";
key_selector_detail::append_byte(s, static_cast<unsigned>(b));
s += " -> key ";
key_selector_detail::append_uint(s, window.window_to_key[b]);
s += "\n";
}
}
s += " step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
} else {
// Mirrors the perfect-hash path of match_raw().
if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
s += "algorithm: hash-and-displace perfect hash\n";
s += " step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
s += " step 2: keyhash = base-31 rolling hash of the length and the first ";
key_selector_detail::append_uint(s, phf.hd_hash_variant);
s += " bytes\n";
s += " step 3: slot = (displacement[bucket] + keyhash) mod ";
key_selector_detail::append_uint(s, table_size);
s += "\n step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
? key_selector_detail::hd_key_hash_2(k)
: key_selector_detail::hd_key_hash_4(k);
std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
s += " \"";
s += k;
s += "\": bucket=";
key_selector_detail::append_uint(s, bucket);
s += " displacement=";
key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
s += " keyhash=";
key_selector_detail::append_uint(s, kh);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
} else {
s += "algorithm: gperf-style perfect hash over ";
key_selector_detail::append_uint(s, phf.num_positions);
s += " character position(s)\n";
s += " step 1: h = key length\n";
s += " step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
for (std::size_t i = 0; i < phf.num_positions; ++i) {
s += " position ";
if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
s += "last character";
} else {
s += "byte index ";
key_selector_detail::append_uint(s, phf.positions[i]);
}
s += "\n";
}
s += " step 3: slot = h mod ";
key_selector_detail::append_uint(s, table_size);
s += " (a power of two, applied as a bitmask)\n";
s += " step 4: slot_to_key[slot] gives the candidate key index\n";
s += " per-key derivation:\n";
for (std::size_t i = 0; i < N; ++i) {
std::string_view k = keys[i];
std::size_t h = k.size();
for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
std::size_t pos = phf.positions[pi];
std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
? (k.size() - 1) : pos;
if (idx < k.size()) {
h += phf.asso_values[pi][static_cast<unsigned char>(k[idx])];
}
}
std::size_t slot = h & (table_size - 1);
s += " \"";
s += k;
s += "\": h=";
key_selector_detail::append_uint(s, h);
s += " slot=";
key_selector_detail::append_uint(s, slot);
s += "\n";
}
}
s += " occupied slots (slot -> key):\n";
for (std::size_t slot = 0; slot < table_size; ++slot) {
if (phf.slot_to_key[slot] < N) {
s += " slot ";
key_selector_detail::append_uint(s, slot);
s += " -> key ";
key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
s += " (\"";
s += keys[phf.slot_to_key[slot]];
s += "\", length ";
key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
s += ")\n";
}
}
s += " confirm the candidate by checking the key length matches and comparing the key bytes\n";
}
return s;
}
};
namespace key_selector_detail {
template <typename> struct is_key_selector : std::false_type {};
template <constevalutil::fixed_string... Keys>
struct is_key_selector<key_selector<Keys...>> : std::true_type {};
} // namespace key_selector_detail
/**
* Matches any instantiation of key_selector<Keys...>. Used to constrain
* object::for_each so that passing a non-selector type yields a clear
* constraint error rather than a cascade of failures inside for_each.
*/
template <typename T>
concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_SUPPORTS_CONCEPTS
#endif // SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
/* end file simdjson/generic/ondemand/key_selector.h for rvv_vls */
/* including simdjson/generic/ondemand/object.h for rvv_vls: #include "simdjson/generic/ondemand/object.h" */
/* begin file simdjson/generic/ondemand/object.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/key_selector.h" */
/* amalgamation skipped (editor-only): #include <vector> */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION && SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Result of object::for_each: the first error encountered (SUCCESS if none) and
* the number of distinct selector keys that matched during the walk. A
* matched_count equal to Selector::size() means every selected key was present
* in the object. Implicitly converts to error_code so existing callers that only
* care about the error (including SIMDJSON_TRY and the test ASSERT_* macros) keep
* working unchanged.
*
* On error paths (a handler returns a non-SUCCESS error_code, or a direct-target
* deserialization via value::get fails), matched_count reflects only the number
* of distinct selected keys that were successfully handled *before* the failure;
* the failing key is not counted. This is the current behavior.
*
* Marked [[nodiscard]]: for_each surfaces parse/type errors (from a matched
* value, a returning handler, or the structural walk itself) only through this
* result, so it must not be silently dropped. This matters most in builds
* without exceptions, where it is the only channel for those errors. To
* deliberately ignore it, assign to a variable or cast to void.
*/
struct [[nodiscard]] for_each_result {
error_code error{SUCCESS};
std::size_t matched_count{0};
constexpr operator error_code() const noexcept { return error; }
};
namespace key_selector_for_each_detail {
/**
* A per-key handler for the variadic object::for_each is either:
* - an invocable taking a value (run custom logic for that field), or
* - a deserialization target T, in which case the matched value is assigned
* directly via value::get(T&).
* The target form lets callers bind fields straight to variables without a
* lambda per key -- e.g. obj.for_each<"name","city","age">(name, city, age) --
* while still allowing a lambda in any position when custom logic is needed
* (for example to descend into a nested object).
*/
template <typename H>
concept field_handler =
std::is_invocable_v<std::remove_reference_t<H>&, value> ||
::simdjson::deserializable<std::remove_cvref_t<H>, value>;
/**
* noexcept-ness of handling a single handler: nothrow-invocability for the
* callback form, or nothrow-deserializability for the direct-target form
* (builtin scalar/string targets are noexcept; custom targets follow their
* own tag_invoke noexcept specification).
*/
template <typename H>
inline constexpr bool nothrow_field_handler_v =
std::is_invocable_v<std::remove_reference_t<H>&, value>
? std::is_nothrow_invocable_v<std::remove_reference_t<H>&, value>
: ::simdjson::nothrow_deserializable<std::remove_cvref_t<H>, value>;
/**
* nothrow_field_handler_v folded over a whole handler pack. The variadic
* for_each overloads spell their conditional-noexcept specifier with this
* single id-expression rather than an inline fold expression. MSVC (VS18)
* mishandles a fold-expression that appears directly in the noexcept-specifier
* of an out-of-line template definition: it fails to recognize the definition's
* specifier as matching the in-class declaration's and reports a spurious
* C2382 ("redefinition; different exception specifications"). Naming the fold
* here keeps the specifier a plain identifier, which matches reliably.
*/
template <typename... Handlers>
inline constexpr bool nothrow_field_handlers_v =
(nothrow_field_handler_v<Handlers> && ...);
} // namespace key_selector_for_each_detail
#endif
/**
* An opaque snapshot of an object's scanning position, obtained from
* object::get_current_position() and consumed by object::revert_position().
*
* This bundles the raw token position with the iteration depth that was
* live at the moment of capture: a scalar field value (e.g. a number)
* unwinds back to the object's own depth once fully consumed, while a
* compound value (array/object) not yet fully consumed stays one level
* deeper. The correct depth to restore to therefore depends on what was
* live when the snapshot was taken, not on the object itself, which is
* why both pieces travel together here rather than being recomputed later.
*
* The members are private and only constructible by object itself: a
* hand-built value here would let revert_position() jump to an arbitrary,
* unvalidated position and depth, and this type carries no way to check
* that a given snapshot still refers to a live object. Only ever pass
* along a value you got from get_current_position(), and only back to the
* same object, before that object has been reset() or the parser has
* iterate()d a new document: neither invalidates a snapshot in a way this
* type can detect.
*/
struct object_position {
/**
* Default-constructed so a variable can be declared and assigned later,
* matching e.g. document()/object(). Not a valid position to revert to.
*/
simdjson_inline object_position() noexcept = default;
private:
token_position position{};
depth_t depth{};
simdjson_inline object_position(token_position position_, depth_t depth_) noexcept
: position(position_), depth(depth_) {}
friend class object;
};
/**
* A forward-only JSON object field iterator.
*/
class object {
public:
/**
* Create a new invalid object.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object() noexcept = default;
/**
* Get an iterator to the start of the object. We recommend using a range-based for loop.
*
* Using the iterator directly is also possible but error-prone and discouraged. In particular,
* you must dereference the iterator exactly once per iteration (before calling '++').
* Doing otherwise is unsafe and may lead to errors. You are responsible for ensuring
*
* With SIMDJSON_DEVELOPMENT_CHECKS, the iterator locks this object while it is
* alive, so that reentrant access (find_field(), reset(), ...) is reported as
* OUT_OF_ORDER_ITERATION.
*/
simdjson_inline simdjson_result<object_iterator> begin() & noexcept;
/**
* Get an iterator to the start of a temporary object, e.g., `v.get_object().begin()`.
*
* The iterator does not depend on the object instance and may outlive it, so
* it does not lock it.
*/
simdjson_inline simdjson_result<object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<object_iterator> end() noexcept;
/**
* Look up a field by name on an object (order-sensitive). By order-sensitive, we mean that
* fields must be accessed in the order they appear in the JSON text (although you can
* skip fields). See find_field_unordered() and operator[] for an order-insensitive version.
*
* The following code reads z, then y, then x, and thus will not retrieve x or y if fed the
* JSON `{ "x": 1, "y": 2, "z": 3 }`:
*
* ```cpp
* simdjson::ondemand::parser parser;
* auto json = R"( { "x": 1, "y": 2, "z": 3 } )"_padded;
* auto doc = parser.iterate(json);
* double z = doc.find_field("z");
* double y = doc.find_field("y");
* double x = doc.find_field("x");
* ```
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys.
* e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a
* key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string()
* is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field(std::string_view key) && noexcept;
/**
* Look up a field by name on an object, without regard to key order.
*
* **Performance Notes:** This is a bit less performant than find_field(), though its effect varies
* and often appears negligible. It starts out normally, starting out at the last field; but if
* the field is not found, it scans from the beginning of the object to see if it missed it. That
* missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object
* in question is large. The fact that the extra code is there also bumps the executable size.
*
* We default operator[] on find_field_unordered() for convenience.
* It is the default because it would be highly surprising (and hard to debug) if the
* default behavior failed to look up a field just because it was in the wrong order--and many
* APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order.
*
* Use find_field() if you are sure fields will be in order (or are willing to treat it as if the
* field was not there when they are not in order).
*
* If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful
* that only one field is returned.
*
* You must consume the fields on an object one at a time. A request for a new key
* invalidates previous field values: it makes them unsafe. The value instance you get
* from `content["bids"]` becomes invalid when you call `content["asks"]`. The array
* given by content["bids"].get_array() should not be accessed after you have called
* content["asks"].get_array(). You can detect such mistakes by first compiling and running
* the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an
* OUT_OF_ORDER_ITERATION error is generated.
*
* You are expected to access keys only once. You should access the value corresponding to a key
* a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error.
*
* If you expect to have keys with escape characters, please review our documentation.
*
* @param key The key to look up.
* @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object.
*/
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) && noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) & noexcept;
/** @overload simdjson_inline simdjson_result<value> find_field_unordered(std::string_view key) & noexcept; */
simdjson_inline simdjson_result<value> operator[](std::string_view key) && noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Walk this object once and invoke on_match(selector_index, value) for each
* field whose key is in the compile-time key_selector Selector, in JSON order
* (first occurrence of a duplicate key wins). Iteration stops once all
* Selector::size() keys have matched or the object ends. The value is consumed
* in place, so this is a low-overhead way to extract a known set of fields
* regardless of their order in the JSON.
*
* Like other object iteration in simdjson, for_each consumes the object by
* advancing the underlying iterator state; after the call the same object
* instance should not be used for further field access or iteration.
*
* Usage:
* using sel_t = ondemand::key_selector<"id", "text", "user">;
* obj.for_each<sel_t>([&](std::size_t i, ondemand::value v) {
* switch (i) { case 0: ...; case 1: ...; }
* });
*
* Limitations (see key_selector): each key must be at most 63 characters long,
* and the number of keys should be moderate (hard limit 255; a handful is
* best, as the compile-time perfect hash may fail or slow compilation for
* large key sets). The keys must be distinct, non-empty, and free of backslash, double-quote and
* null bytes.
*
* The callback may return either void or an error_code. When it returns an
* error_code, the walk stops at the first non-SUCCESS result and that error is
* returned, which lets the callback surface value-parse errors.
*
* This function is conditionally noexcept: it is noexcept exactly when invoking
* the callback is noexcept. The callback runs inside this frame, so a throwing
* callback (e.g. one using the exception-throwing conversions like
* std::string_view(value) or uint64_t(value)) makes for_each potentially
* throwing too -- the exception propagates to the caller instead of crossing a
* noexcept boundary and calling std::terminate.
*
* @returns a for_each_result holding the first error encountered while walking
* the object (including any error returned by the callback, SUCCESS if
* none) and the number of distinct selector keys that matched. The
* result converts implicitly to error_code, so callers that only need
* the error can ignore the count.
*/
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_inline for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>);
/**
* Variadic per-key form. Provide exactly one handler per key in the Selector
* (compiler-enforced). Handlers are processed in JSON document order for the
* matching keys. Each handler is either:
* - a deserialization target (a variable), in which case the matched value
* is assigned to it via value::get -- no lambda required; or
* - an invocable taking the ondemand::value (for custom logic such as
* descending into a nested object). It may return void or error_code;
* returning error_code lets you surface parse/type errors.
* The two styles may be mixed freely, one handler per key.
*
* Example (bind fields straight to variables):
* using fields = ondemand::key_selector<"name", "city", "age">;
* obj.for_each<fields>(name, city, age);
*
* Example (mixing a target and a lambda):
* obj.for_each<ondemand::key_selector<"id", "user">>(
* id, // assigned via value::get
* [&](ondemand::value v){ u = read_user(v); } // custom logic
* );
*
* The index-based single-callback form (taking (size_t, value)) remains
* available for shared-state or more complex per-key logic.
*/
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Direct-key shorthand. Equivalent to for_each<key_selector<Keys...>>(...).
* Lets you write the keys inline without a separate using/alias, binding each
* field straight to a variable (or a lambda, see the Selector form above):
*
* obj.for_each<"name", "city", "age">(name, city, age);
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#endif
/**
* Get the value associated with the given JSON pointer. We use the RFC 6901
* https://tools.ietf.org/html/rfc6901 standard, interpreting the current node
* as the root of its own JSON document.
*
* ondemand::parser parser;
* auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("/foo/a/1") == 20
*
* It is allowed for a key to be the empty string:
*
* ondemand::parser parser;
* auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded;
* auto doc = parser.iterate(json);
* doc.at_pointer("//a/1") == 20
*
* Note that at_pointer() called on the document automatically calls the document's rewind
* method between each call. It invalidates all previously accessed arrays, objects and values
* that have not been consumed. Yet it is not the case when calling at_pointer on an object
* instance: there is no rewind and no invalidation.
*
* You may call at_pointer more than once on an object, but each time the pointer is advanced
* to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding
* key (as well as the current key) can no longer be used with following JSON pointer calls.
*
* Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching.
*
* @return The value associated with the given JSON pointer, or:
* - 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
* - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed
*/
inline simdjson_result<value> 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<value> at_path(std::string_view json_path) noexcept;
/**
* Call the provided callback for each value matching the given JSONPath
* expression with wildcard support.
* Supports wildcard patterns like ".*" to match all object fields.
*
* @param json_path JSONPath expression with wildcards
* @param callback Function called for each matching value
* @return error_code indicating success or failure
*/
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) 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
* can iterate through the object more than once. If you unescape a string or a key
* within the object more than once, you have unsafe code. Note that rewinding an object
* means that you may need to reparse it anew: it is not a free operation.
*
* @returns true if the object contains some elements (not empty)
*/
inline simdjson_result<bool> reset() & noexcept;
/**
* Get an opaque token representing the object's current scanning position.
* Pass it to revert_position() to return to this exact point later, without
* paying the cost of a full reset() and re-scan from the beginning.
*
* A typical use is an optional field that may or may not be next: capture
* the position, attempt find_field(), and on NO_SUCH_FIELD, revert_position()
* instead of reset() so that fields already consumed are not rescanned.
*
* The returned token is only valid for this object, and only until it is
* reset() or the parser iterate()s a new document; using it after either
* is undefined behavior (see object_position).
*
* @returns An opaque position token.
*/
simdjson_inline object_position get_current_position() const noexcept;
/**
* Return the object's scanning position to a snapshot previously obtained
* from get_current_position(). Unlike reset(), this does not rescan the
* object from the beginning: fields before the captured position remain
* consumed, and scanning resumes exactly where the snapshot was captured.
*
* @param position A snapshot previously returned by get_current_position(),
* for this same object.
* @returns SUCCESS, or OUT_OF_ORDER_ITERATION if called during active
* iteration (SIMDJSON_DEVELOPMENT_CHECKS builds only).
*/
simdjson_inline error_code revert_position(object_position position) noexcept;
/**
* This method scans the beginning of the object and checks whether the
* object is empty.
* The runtime complexity is constant time. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*/
inline simdjson_result<bool> is_empty() & noexcept;
/**
* This method scans the object and counts the number of key-value pairs.
* The count_fields method should always be called before you have begun
* iterating through the object: it is expected that you are pointing at
* the beginning of the object.
* The runtime complexity is linear in the size of the object. After
* calling this function, if successful, the object is 'rewinded' at its
* beginning as if it had never been accessed. If the JSON is malformed (e.g.,
* there is a missing comma), then an error is returned and it is no longer
* safe to continue.
*
* To check that an object is empty, it is more performant to use
* the is_empty() method.
*
* Performance hint: You should only call count_fields() as a last
* resort as it may require scanning the document twice or more.
*/
simdjson_inline simdjson_result<size_t> count_fields() & noexcept;
/**
* Consumes the object and returns a string_view instance corresponding to the
* object as represented in JSON. It points inside the original byte array containing
* the JSON document.
*/
simdjson_inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
/**
* Get this object as the given type.
*
* @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized.
* @returns INCORRECT_TYPE If the JSON object is not of the given type.
* @returns SUCCESS If the parse succeeded and the out parameter was set to the value.
*/
template <typename T>
simdjson_warn_unused simdjson_inline error_code get(T &out)
noexcept(nothrow_gettable<T, object>) {
static_assert(custom_deserializable<T, object>);
return deserialize(*this, out);
}
/**
* Get this array as the given type.
*
* @returns A value of the given type, parsed from the JSON.
* @returns INCORRECT_TYPE If the JSON value is not the given type.
*/
template <typename T>
simdjson_inline simdjson_result<T> get()
noexcept(nothrow_gettable<T, object>)
{
static_assert(std::is_default_constructible<T>::value, "The specified type is not default constructible.");
T out{};
SIMDJSON_TRY(get<T>(out));
return out;
}
#if SIMDJSON_STATIC_REFLECTION
/**
* Extract only specific fields from the JSON object into a struct.
*
* This allows selective deserialization of only the fields you need,
* potentially improving performance by skipping unwanted fields.
*
* Example:
* ```cpp
* struct Car {
* std::string make;
* std::string model;
* int year;
* double price;
* };
*
* Car car;
* object.extract_into<"make", "model">(car);
* // Only 'make' and 'model' fields are extracted from JSON
* ```
*
* @tparam FieldNames Compile-time string literals specifying which fields to extract
* @param out The output struct to populate with selected fields
* @returns SUCCESS on success, or an error code if a required field is missing or has wrong type
*/
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) & noexcept;
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
protected:
/**
* Go to the end of the object, no matter where you are right now.
*/
simdjson_warn_unused simdjson_inline error_code consume() noexcept;
static simdjson_inline simdjson_result<object> start(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> start_root(value_iterator &iter) noexcept;
static simdjson_inline simdjson_result<object> started(value_iterator &iter) noexcept;
static simdjson_inline object resume(const value_iterator &iter) noexcept;
simdjson_inline object(const value_iterator &iter) noexcept;
simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept;
value_iterator iter{};
#if SIMDJSON_DEVELOPMENT_CHECKS
bool locked{false};
simdjson_inline void set_locked(bool _locked) noexcept;
#endif
friend class value;
friend class document;
friend struct simdjson_result<object>;
#if SIMDJSON_DEVELOPMENT_CHECKS
friend class object_iterator;
friend struct simdjson_result<object_iterator>;
#endif
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::object> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::object> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::object &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator> begin() & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator> begin() && noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator> end() noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field(std::string_view key) && noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> find_field_unordered(std::string_view key) && noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator[](std::string_view key) & noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> operator[](std::string_view key) && noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_pointer(std::string_view json_pointer) noexcept;
simdjson_inline simdjson_result<rvv_vls::ondemand::value> at_path(std::string_view json_path) noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept;
inline simdjson_result<bool> reset() noexcept;
inline simdjson_result<rvv_vls::ondemand::object_position> get_current_position() noexcept;
inline error_code revert_position(rvv_vls::ondemand::object_position position) noexcept;
inline simdjson_result<bool> is_empty() noexcept;
inline simdjson_result<size_t> count_fields() & noexcept;
inline simdjson_result<std::string_view> raw_json() noexcept;
#if SIMDJSON_SUPPORTS_CONCEPTS
// TODO: move this code into object-inl.h
template<typename T>
simdjson_inline simdjson_result<T> get() noexcept(nothrow_gettable<T, rvv_vls::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, rvv_vls::ondemand::object>) {
return first;
}
return first.get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code get(T& out) noexcept(nothrow_gettable<T, rvv_vls::ondemand::object>) {
if (error()) { return error(); }
if constexpr (std::is_same_v<T, rvv_vls::ondemand::object>) {
out = first;
} else {
SIMDJSON_TRY( first.get<T>(out) );
}
return SUCCESS;
}
/**
* Forwards to object::for_each on the underlying object, so error-code-style
* chains (e.g. doc["x"].get_object()) can call for_each without first
* extracting the object. If this result holds an error, that error is returned
* (with a zero match count) and the callback is not invoked. See
* object::for_each for the semantics.
*/
template <typename Selector, typename Func>
requires rvv_vls::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, rvv_vls::ondemand::value>
simdjson_inline rvv_vls::ondemand::for_each_result for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, rvv_vls::ondemand::value>);
/**
* Forwarding overload for the variadic per-key form (targets and/or lambdas).
*/
template <typename Selector, typename... Handlers>
requires rvv_vls::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(rvv_vls::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline rvv_vls::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(rvv_vls::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
/**
* Forwarding overload for the direct-key variadic form.
*/
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(rvv_vls::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline rvv_vls::ondemand::for_each_result for_each(Handlers&&... on_match)
noexcept(rvv_vls::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>);
#if SIMDJSON_STATIC_REFLECTION
// TODO: move this code into object-inl.h
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code extract_into(T& out) noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_SUPPORTS_CONCEPTS
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_H
/* end file simdjson/generic/ondemand/object.h for rvv_vls */
/* including simdjson/generic/ondemand/object_iterator.h for rvv_vls: #include "simdjson/generic/ondemand/object_iterator.h" */
/* begin file simdjson/generic/ondemand/object_iterator.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
class object_iterator {
public:
/**
* Create a new invalid object_iterator.
*
* Exists so you can declare a variable and later assign to it before use.
*/
simdjson_inline object_iterator() noexcept = default;
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline ~object_iterator() noexcept;
simdjson_inline object_iterator(object_iterator&&) noexcept;
simdjson_inline object_iterator& operator=(object_iterator&&) noexcept;
simdjson_inline object_iterator(const object_iterator&) noexcept;
simdjson_inline object_iterator& operator=(const object_iterator&) noexcept;
#endif
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
// MUST ONLY BE CALLED ONCE PER ITERATION.
simdjson_inline simdjson_result<field> operator*() noexcept;
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const object_iterator &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const object_iterator &) const noexcept;
// Checks for ']' and ','
// YOU MUST NOT CALL THIS IF operator* YIELDED AN ERROR.
// YOU MUST NOT CALL THIS WITHOUT A CORRESPONDING operator* CALL.
simdjson_inline object_iterator &operator++() noexcept;
private:
#if SIMDJSON_DEVELOPMENT_CHECKS
bool has_been_referenced{false};
object* parent{nullptr};
simdjson_inline object_iterator(const value_iterator &_iter, object* _parent) noexcept;
#endif
/**
* The underlying JSON iterator.
*
* PERF NOTE: expected to be elided in favor of the parent document: this is set when the object
* is first used, and never changes afterwards.
*/
value_iterator iter{};
simdjson_inline object_iterator(const value_iterator &iter) noexcept;
friend struct simdjson_result<object_iterator>;
friend class object;
};
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
template<>
struct simdjson_result<rvv_vls::ondemand::object_iterator> : public rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::object_iterator> {
public:
simdjson_inline simdjson_result(rvv_vls::ondemand::object_iterator &&value) noexcept; ///< @private
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
simdjson_inline simdjson_result() noexcept = default;
//
// Iterator interface
//
// Reads key and value, yielding them to the user.
simdjson_inline simdjson_result<rvv_vls::ondemand::field> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
// Assumes it's being compared with the end. true if depth < iter->depth.
simdjson_inline bool operator==(const simdjson_result<rvv_vls::ondemand::object_iterator> &) const noexcept;
// Assumes it's being compared with the end. true if depth >= iter->depth.
simdjson_inline bool operator!=(const simdjson_result<rvv_vls::ondemand::object_iterator> &) const noexcept;
// Checks for ']' and ','
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator> &operator++() noexcept;
};
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_H
/* end file simdjson/generic/ondemand/object_iterator.h for rvv_vls */
/* including simdjson/generic/ondemand/ranges.h for rvv_vls: #include "simdjson/generic/ondemand/ranges.h" */
/* begin file simdjson/generic/ondemand/ranges.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* A ranges-compatible iterator adapter for JSON arrays.
*
* Wraps array_iterator to satisfy std::input_iterator by providing:
* - const operator* (via mutable internal state)
* - post-increment operator
* - iterator_concept tag
*
* The mutable approach is standard for single-pass input iterators that
* read from external sources (similar to std::istream_iterator).
*/
class array_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<value>;
using reference = simdjson_result<value>;
using difference_type = std::ptrdiff_t;
simdjson_inline array_range_iterator() noexcept = default;
simdjson_inline explicit array_range_iterator(array_iterator iter) noexcept;
/**
* Get the current element. Const-qualified for std::indirectly_readable;
* internally delegates to the mutable wrapped iterator.
*/
simdjson_inline simdjson_result<value> operator*() const noexcept;
simdjson_inline array_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
/**
* Comparison delegates to array_iterator::operator==, which checks
* whether the underlying parser has finished the array (depth-based).
*/
simdjson_inline friend bool operator==(const array_range_iterator& a,
const array_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable array_iterator iter_{};
};
/**
* A std::ranges::view over a JSON array.
*
* Wraps an ondemand::array and exposes begin()/end() that return
* array_range_iterator (satisfying std::input_iterator), enabling
* use with std::views::transform and other range adaptors.
*
* If the array's begin() returns an error (only possible under
* SIMDJSON_DEVELOPMENT_CHECKS), the range will be empty and error()
* will return the error code.
*
* Usage:
* ondemand::parser parser;
* auto doc = parser.iterate(json);
* auto arr = doc.get_array().value();
* for (auto elem : ondemand::get_range(arr)) { ... }
*/
class array_range {
public:
simdjson_inline array_range() noexcept = default;
simdjson_inline explicit array_range(array& arr) noexcept;
simdjson_inline array_range_iterator begin() noexcept;
simdjson_inline array_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
array_iterator begin_{};
array_iterator end_{};
error_code error_{SUCCESS};
};
/**
* A ranges-compatible iterator adapter for JSON objects.
*
* Wraps object_iterator to satisfy std::input_iterator, yielding
* simdjson_result<field> elements (key-value pairs).
*/
class object_range_iterator {
public:
using iterator_concept = std::input_iterator_tag;
using value_type = simdjson_result<field>;
using reference = simdjson_result<field>;
using difference_type = std::ptrdiff_t;
simdjson_inline object_range_iterator() noexcept = default;
simdjson_inline explicit object_range_iterator(object_iterator iter) noexcept;
simdjson_inline simdjson_result<field> operator*() const noexcept;
simdjson_inline object_range_iterator& operator++() noexcept;
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void operator++(int) noexcept;
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline friend bool operator==(const object_range_iterator& a,
const object_range_iterator& b) noexcept {
return a.iter_ == b.iter_;
}
private:
mutable object_iterator iter_{};
};
/**
* A std::ranges::view over a JSON object.
*
* Wraps an ondemand::object and exposes begin()/end() that return
* object_range_iterator, enabling use with range adaptors.
*
* If the object's begin() returns an error, the range will be empty
* and error() will return the error code.
*/
class object_range {
public:
simdjson_inline object_range() noexcept = default;
simdjson_inline explicit object_range(object& obj) noexcept;
simdjson_inline object_range_iterator begin() noexcept;
simdjson_inline object_range_iterator end() noexcept;
/** Returns SUCCESS if the range was created successfully, or the error code otherwise. */
simdjson_inline error_code error() const noexcept { return error_; }
private:
object_iterator begin_{};
object_iterator end_{};
error_code error_{SUCCESS};
};
/** Get a std::ranges compatible view over a JSON array. */
simdjson_inline array_range get_range(array& arr) noexcept;
/** Get a std::ranges compatible view over a JSON object (key-value pairs). */
simdjson_inline object_range get_key_value_range(object& obj) noexcept;
#if SIMDJSON_EXCEPTIONS
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline array_range get_range(simdjson_result<array> result);
/** Get a std::ranges compatible view, unwrapping the simdjson_result (throws on error). */
simdjson_inline object_range get_key_value_range(simdjson_result<object> result);
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace std {
namespace ranges {
template<>
inline constexpr bool enable_view<simdjson::rvv_vls::ondemand::array_range> = true;
template<>
inline constexpr bool enable_view<simdjson::rvv_vls::ondemand::object_range> = true;
} // namespace ranges
} // namespace std
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_H
/* end file simdjson/generic/ondemand/ranges.h for rvv_vls */
/* including simdjson/generic/ondemand/serialization.h for rvv_vls: #include "simdjson/generic/ondemand/serialization.h" */
/* begin file simdjson/generic/ondemand/serialization.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
/**
* Create a string-view instance out of a document instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(rvv_vls::ondemand::document& x) noexcept;
/**
* Create a string-view instance out of a value instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. The value must
* not have been accessed previously. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(rvv_vls::ondemand::value& x) noexcept;
/**
* Create a string-view instance out of an object instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(rvv_vls::ondemand::object& x) noexcept;
/**
* Create a string-view instance out of an array instance. The string-view instance
* contains JSON text that is suitable to be parsed as JSON again. It does not
* validate the content.
*/
inline simdjson_result<std::string_view> to_json_string(rvv_vls::ondemand::array& x) noexcept;
inline simdjson_result<std::string_view> to_json_string(simdjson_result<rvv_vls::ondemand::document> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<rvv_vls::ondemand::value> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<rvv_vls::ondemand::object> x);
inline simdjson_result<std::string_view> to_json_string(simdjson_result<rvv_vls::ondemand::array> x);
#if SIMDJSON_STATIC_REFLECTION
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
*/
template<typename T>
requires(!std::same_as<T, rvv_vls::ondemand::document> &&
!std::same_as<T, rvv_vls::ondemand::value> &&
!std::same_as<T, rvv_vls::ondemand::object> &&
!std::same_as<T, rvv_vls::ondemand::array>)
inline std::string to_json_string(const T& obj);
#endif
} // namespace simdjson
/**
* We want to support argument-dependent lookup (ADL).
* Hence we should define operator<< in the namespace
* where the argument (here value, object, etc.) resides.
* Credit: @madhur4127
* See https://github.com/simdjson/simdjson/issues/1768
*/
namespace simdjson { namespace rvv_vls { namespace ondemand {
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The element.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::ondemand::value x);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::rvv_vls::ondemand::value> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::ondemand::array value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::rvv_vls::ondemand::array> x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The array.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::ondemand::document& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::rvv_vls::ondemand::document>&& x);
#endif
inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::ondemand::document_reference& value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::rvv_vls::ondemand::document_reference>&& x);
#endif
/**
* Print JSON to an output stream. It does not
* validate the content.
*
* @param out The output stream.
* @param value The object.
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
*/
inline std::ostream& operator<<(std::ostream& out, simdjson::rvv_vls::ondemand::object value);
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::rvv_vls::ondemand::object> x);
#endif
}}} // namespace simdjson::rvv_vls::ondemand
#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_H
/* end file simdjson/generic/ondemand/serialization.h for rvv_vls */
// Deserialization for standard types
/* including simdjson/generic/ondemand/std_deserialize.h for rvv_vls: #include "simdjson/generic/ondemand/std_deserialize.h" */
/* begin file simdjson/generic/ondemand/std_deserialize.h for rvv_vls */
#if SIMDJSON_SUPPORTS_CONCEPTS
#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/annotations.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <concepts>
#include <limits>
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <vector>
// #include <static_reflection> // for std::define_static_string - header not available yet
#endif
namespace simdjson {
//////////////////////////////
// Number deserialization
//////////////////////////////
template <std::unsigned_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
uint64_t x;
SIMDJSON_TRY(val.get_uint64().get(x));
if (x > (limits::max)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
template <std::floating_point T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
if constexpr (std::is_same_v<T, float>) {
// Going through binary64 and then rounding to binary32 would round twice
// and could produce a value that is not the float nearest to the JSON
// number, so we parse to binary32 directly.
return val.get_float().get(out);
#if SIMDJSON_SUPPORTS_FLOAT32_T
} else if constexpr (std::is_same_v<T, std::float32_t>) {
// Same reason as float.
float x;
SIMDJSON_TRY(val.get_float().get(x));
out = static_cast<T>(x);
return SUCCESS;
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
} else {
double x;
SIMDJSON_TRY(val.get_double().get(x));
out = static_cast<T>(x);
return SUCCESS;
}
}
template <std::signed_integral T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept {
using limits = std::numeric_limits<T>;
int64_t x;
SIMDJSON_TRY(val.get_int64().get(x));
if (x > (limits::max)() || x < (limits::min)()) {
return NUMBER_OUT_OF_RANGE;
}
out = static_cast<T>(x);
return SUCCESS;
}
//////////////////////////////
// String deserialization
//////////////////////////////
// just a character!
error_code tag_invoke(deserialize_tag, auto &val, char &out) noexcept {
std::string_view x;
SIMDJSON_TRY(val.get_string().get(x));
if(x.size() != 1) {
return INCORRECT_TYPE;
}
out = x[0];
return SUCCESS;
}
// any string-like type (can be constructed from std::string_view)
template <concepts::constructible_from_string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::string_view>) {
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::string): building a temporary and
// move-assigning it is markedly slower.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
// any C++20 char8_t string-like type (can be constructed from std::u8string_view),
// such as std::u8string
template <concepts::constructible_from_u8string_view T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(std::is_nothrow_constructible_v<T, std::u8string_view>) {
std::u8string_view str;
SIMDJSON_TRY(val.get_u8string().get(str));
if constexpr (requires { out.assign(str.data(), str.size()); }) {
// Copy straight into out (e.g., std::u8string), as for std::string above.
out.assign(str.data(), str.size());
} else {
out = T{str};
}
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
namespace details {
// Whether to deserialize the elements of the container T directly into a new
// element (emplace_one(out) and then get) rather than into a temporary that is
// then moved into the container. A temporary of a trivially copyable type lives
// in registers and is cheap to move, and value-initializing a small element in
// place costs more than it saves (GCC zeroes it with rep stos). But moving a
// large element, or a string (whose deserialization then needs a temporary
// string and a move assignment of its own), is expensive.
template <typename T>
concept deserialize_in_place =
concepts::returns_reference<T> && requires(T &c) { c.pop_back(); } &&
!std::is_trivially_copyable_v<typename T::value_type> &&
(sizeof(typename T::value_type) > 32 || concepts::constructible_from_string_view<typename T::value_type>);
// Removes the last element of the container on destruction while armed.
template <typename T>
struct pop_back_guard {
T &container;
bool armed{true};
~pop_back_guard() {
if (armed) { container.pop_back(); }
}
};
} // namespace details
/**
* STL containers have several constructors including one that takes a single
* size argument. Thus, some compilers (Visual Studio) will not be able to
* disambiguate between the size and container constructor. Users should
* explicitly specify the type of the container as needed: e.g.,
* doc.get<std::vector<int>>().
*/
template <concepts::appendable_containers T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::value_type;
static_assert(
deserializable<value_type, ValT>,
"The specified type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified type inside the container must default constructible.");
rvv_vls::ondemand::array arr;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, rvv_vls::ondemand::array>) {
arr = val;
} else {
SIMDJSON_TRY(val.get_array().get(arr));
}
if constexpr (std::is_same_v<T, std::vector<value_type>> && !std::is_same_v<value_type, bool>) {
// Collect the elements in a per-thread scratch vector that keeps its
// capacity from call to call, then move them into out after reserving the
// exact size: out is allocated once instead of being regrown. A nested
// array of the same type finds the scratch busy and takes the paths below.
// Prior related work: jsonifier keeps a thread-local vector and sizes the
// caller's vector from that element count (parse_impl.hpp,
// https://github.com/nihilai-collective/Jsonifier).
struct scratch_space {
std::vector<value_type> elements{};
bool busy{false};
};
static thread_local scratch_space scratch;
if (!scratch.busy && out.empty()) {
struct release_scratch {
scratch_space &s;
T &out;
size_t parsed{0};
bool complete{false};
// On an error or an exception, out gets the elements parsed so far (as
// with the loops below), without allocating. Kept out of the hot path.
simdjson_never_inline void keep_parsed() noexcept {
s.elements.resize(parsed);
out.swap(s.elements);
}
~release_scratch() {
if (simdjson_unlikely(!complete)) { keep_parsed(); }
s.elements.clear();
// Do not hold on to the memory of a very large array.
if (s.elements.capacity() * sizeof(value_type) > (1 << 20)) { std::vector<value_type>().swap(s.elements); }
s.busy = false;
}
} release{scratch, out};
scratch.busy = true;
for (auto v : arr) {
SIMDJSON_TRY(v.get<value_type>(scratch.elements.emplace_back()));
release.parsed++;
}
out.reserve(release.parsed);
release.complete = true;
for (auto &e : scratch.elements) { out.emplace_back(std::move(e)); }
return SUCCESS;
}
}
if constexpr (details::deserialize_in_place<T>) {
for (auto v : arr) {
auto &slot = concepts::emplace_one(out);
// An error or an exception (a user tag_invoke may throw) must not leave
// a partially deserialized element behind.
details::pop_back_guard<T> guard{out};
SIMDJSON_TRY(v.get<value_type>(slot));
guard.armed = false;
}
} else {
for (auto v : arr) {
// Deserialize into a temporary first: an error or an exception (a user
// tag_invoke may throw) must not leave a default-constructed element behind.
value_type temp;
if (auto const err = v.get<value_type>(temp); err) {
return err;
}
concepts::emplace_one(out, std::move(temp));
}
}
return SUCCESS;
}
/**
* We want to support std::map and std::unordered_map but only for
* string-keyed types.
*/
template <concepts::string_view_keyed_map T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
static_assert(
deserializable<value_type, ValT>,
"The specified value type inside the container must itself be deserializable");
static_assert(
std::is_default_constructible_v<value_type>,
"The specified value type inside the container must default constructible.");
rvv_vls::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
value_type this_value;
SIMDJSON_TRY(field.value().get<value_type>().get(this_value));
[[maybe_unused]] std::pair<typename T::iterator, bool> result = out.emplace(key, this_value);
// unclear what to do if the key already exists
// if (result.second == false) {
// // key already exists
// }
}
(void)out;
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, rvv_vls::ondemand::object &obj, T &out) noexcept(false) {
using value_type = typename std::remove_cvref_t<T>::mapped_type;
out.clear();
for (auto field : obj) {
std::string_view key;
SIMDJSON_TRY(field.unescaped_key().get(key));
rvv_vls::ondemand::value value_obj;
SIMDJSON_TRY(field.value().get(value_obj));
value_type this_value;
SIMDJSON_TRY(value_obj.get(this_value));
out.emplace(typename T::key_type(key), std::move(this_value));
}
return SUCCESS;
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, rvv_vls::ondemand::value &val, T &out) noexcept(false) {
rvv_vls::ondemand::object obj;
SIMDJSON_TRY(val.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, rvv_vls::ondemand::document &doc, T &out) noexcept(false) {
rvv_vls::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
template <concepts::string_view_keyed_map T>
error_code tag_invoke(deserialize_tag, rvv_vls::ondemand::document_reference &doc, T &out) noexcept(false) {
rvv_vls::ondemand::object obj;
SIMDJSON_TRY(doc.get_object().get(obj));
return simdjson::deserialize(obj, out);
}
/**
* This CPO (Customization Point Object) will help deserialize into
* smart pointers.
*
* @tparam T The type inside the smart pointer
* @tparam ValT document/value type
* @param val document/value
* @param out a reference to the smart pointer
* @return status of the conversion
*/
template <concepts::smart_pointer T, typename ValT>
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
using element_type = typename std::remove_cvref_t<T>::element_type;
// For better error messages, don't use these as constraints on
// the tag_invoke CPO.
static_assert(
deserializable<element_type, ValT>,
"The specified type inside the unique_ptr must itself be deserializable");
static_assert(
std::is_default_constructible_v<element_type>,
"The specified type inside the unique_ptr must default constructible.");
// Own the allocation before get(): a user tag_invoke may throw.
std::unique_ptr<element_type> ptr(new (std::nothrow) element_type());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.template get<element_type>(*ptr));
out = std::move(ptr);
return SUCCESS;
}
/**
* This CPO (Customization Point Object) will help deserialize into optional types.
*/
template <concepts::optional_type T>
error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept(nothrow_deserializable<typename std::remove_cvref_t<T>::value_type, decltype(val)>) {
using value_type = typename std::remove_cvref_t<T>::value_type;
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullopt
return SUCCESS;
}
if (!out) {
out.emplace();
}
SIMDJSON_TRY(val.template get<value_type>(out.value()));
return SUCCESS;
}
#if SIMDJSON_STATIC_REFLECTION
template <typename T>
constexpr bool user_defined_type = (std::is_class_v<T>
&& !std::is_same_v<T, std::string> && !std::is_same_v<T, std::string_view>
#if SIMDJSON_SUPPORTS_CHAR8_T
// The char8_t string types are class types with no reflectable members, so
// without this they would be taken for user structs and the reflection
// overload below would compete with the u8 string overload in
// std_deserialize.h, making every tag_invoke call on them ambiguous.
&& !std::is_same_v<T, std::u8string> && !std::is_same_v<T, std::u8string_view>
#endif // SIMDJSON_SUPPORTS_CHAR8_T
&& !concepts::optional_type<T> &&
!concepts::appendable_containers<T>
// simdjson's own types (array, object, value, raw_json_string, number,
// document, document_reference) have dedicated get<T>() specializations and
// must never go through reflection.
&& !is_builtin_deserializable_v<T>
&& !std::is_same_v<T, rvv_vls::ondemand::number>
&& !std::is_same_v<T, rvv_vls::ondemand::document>
&& !std::is_same_v<T, rvv_vls::ondemand::document_reference>);
// key_selector_reflection_detail is defined unconditionally (it only requires
// static reflection). It provides the compile-time machinery for building a
// key_selector from a struct's members, the per-member helpers shared by every
// deserialization path (they implement the annotations of annotations.h), the
// ordered per-member path used by the opt-out build (see
// deserialize_struct_ordered below), and the scan used for structs annotated
// with deny_unknown_fields and as an automatic fallback (see
// deserialize_struct_scan and keys_fit_selector below).
namespace key_selector_reflection_detail {
// A member participates if it is public, non-const, and not annotated with skip
// or skip_deserializing.
consteval bool is_eligible_member(std::meta::info mem) {
return !std::meta::is_const(mem) && std::meta::is_public(mem)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
&& !simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag);
}
// A field is a data member reached from T through a chain of members: usually a
// direct member of T (a path of length one), or a member of a member annotated
// with flatten (the members of a flattened structure are fields of the enclosing
// one). A field is identified by the type member_path<m1, m2, ..., leaf>.
template <std::meta::info First, std::meta::info... Rest>
struct member_path {
// The data member holding the value; its annotations drive (de)serialization.
static constexpr std::meta::info leaf = [] {
std::meta::info members[] = {First, Rest...};
return members[sizeof...(Rest)];
}();
template <typename T>
static simdjson_inline constexpr auto &get(T &obj) noexcept {
if constexpr (sizeof...(Rest) == 0) {
return obj.[:First:];
} else {
return member_path<Rest...>::get(obj.[:First:]);
}
}
};
// True when T can be flattened: a structure deserialized member by member (not
// a string, a container, an optional, a smart pointer, ...).
template <typename T>
constexpr bool flattenable_type = user_defined_type<T> && !concepts::string_view_keyed_map<T>
&& !concepts::container_but_not_string<T> && !concepts::smart_pointer<T>;
consteval void append_eligible_fields(std::meta::info type, std::vector<std::meta::info> &prefix,
std::vector<std::meta::info> &fields) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (!is_eligible_member(mem)) { continue; }
prefix.push_back(std::meta::reflect_constant(mem));
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
std::meta::info flattened = simdjson::detail::flattened_type(mem);
if (!std::meta::extract<bool>(std::meta::substitute(^^flattenable_type, {flattened}))) {
throw std::meta::exception(u8"simdjson::flatten requires a member whose type is a structure deserialized member by member", mem);
}
append_eligible_fields(flattened, prefix, fields);
} else {
fields.push_back(std::meta::substitute(^^member_path, prefix));
}
prefix.pop_back();
}
}
// The fields of `type` that participate in deserialization, in declaration order
// (the fields of a flattened member take its place). A field's position in this
// list is its "field index" below.
consteval std::vector<std::meta::info> eligible_fields(std::meta::info type) {
std::vector<std::meta::info> prefix;
std::vector<std::meta::info> fields;
append_eligible_fields(type, prefix, fields);
return fields;
}
// The data member at the end of a field path.
consteval std::meta::info field_leaf(std::meta::info path) {
return std::meta::extract<std::meta::info>(std::meta::template_arguments_of(path).back());
}
// Number of fields that participate in deserialization. A class can have zero
// eligible fields (e.g. std::chrono::time_point, whose only data member is
// private): an empty key_selector cannot be built, so the tag_invoke below
// special-cases this count.
template <typename T>
consteval std::size_t eligible_field_count() {
return eligible_fields(^^T).size();
}
// The keys accepted for a member (or an enumerator): its JSON key followed by its
// aliases. They are static strings, so they can be used as template arguments.
consteval std::vector<const char *> accepted_keys_of(std::meta::info entity) {
std::vector<const char *> keys;
for (std::string_view key : simdjson::detail::json_key_names(entity)) {
bool repeated = false;
for (const char *previous : keys) { repeated = repeated || std::string_view(previous) == key; }
if (!repeated) { keys.push_back(std::define_static_string(key)); }
}
return keys;
}
// Every key accepted for T: the eligible fields in order, each followed by its
// aliases. This is the key list of T's key_selector.
consteval std::vector<const char *> accepted_keys(std::meta::info type) {
std::vector<const char *> keys;
for (std::meta::info path : eligible_fields(type)) {
for (const char *key : accepted_keys_of(field_leaf(path))) { keys.push_back(key); }
}
return keys;
}
// The field index of each entry of accepted_keys(type).
consteval std::vector<std::size_t> accepted_key_fields(std::meta::info type) {
std::vector<std::size_t> key_fields;
std::vector<std::meta::info> fields = eligible_fields(type);
for (std::size_t i = 0; i < fields.size(); ++i) {
for (std::size_t j = 0; j < accepted_keys_of(field_leaf(fields[i])).size(); ++j) { key_fields.push_back(i); }
}
return key_fields;
}
// True when no two eligible fields of T accept the same key. Otherwise one JSON
// key would have to fill several members: this is reported at compile time.
template <typename T>
consteval bool accepted_keys_are_distinct() {
std::vector<const char *> keys = accepted_keys(^^T);
for (std::size_t i = 0; i < keys.size(); ++i) {
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (std::string_view(keys[i]) == std::string_view(keys[j])) { return false; }
}
}
return true;
}
// True when some accepted key of T is written with escape sequences in JSON (a
// double quote, a backslash or a control character). Such a key can only be
// matched by comparing unescaped keys (deserialize_struct_scan): obj[key] and
// the key_selector compare the raw bytes.
template <typename T>
consteval bool keys_need_unescaping() {
for (std::string_view key : accepted_keys(^^T)) {
for (char c : key) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return true; }
}
}
return false;
}
// True when some eligible field of T has aliases: several selector keys may then
// map to the same field.
template <typename T>
consteval bool has_aliases() {
return accepted_keys(^^T).size() != eligible_fields(^^T).size();
}
// True when `mem` is annotated with default_value or default_from (directly, or
// through a default_value annotation on the enclosing structure).
template <auto mem>
consteval bool has_default() {
return simdjson::detail::has_annotation(mem, ^^simdjson::detail::default_value_tag)
|| simdjson::detail::has_annotation(std::meta::parent_of(mem), ^^simdjson::detail::default_value_tag)
|| simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t) != std::meta::info{};
}
// True when a missing key for `mem` is not an error: optional members and
// members with a default.
template <auto mem>
consteval bool may_be_absent() {
return concepts::optional_type<typename [: std::meta::type_of(mem) :]> || has_default<mem>();
}
// True when every eligible field of T is required (none may be absent). In that
// case presence can be checked with a single match count instead of a per-field
// "seen" array.
template <typename T>
consteval bool all_eligible_fields_required() {
bool all_required = true;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
if constexpr (may_be_absent<[: path :]::leaf>()) {
all_required = false;
}
}
return all_required;
}
// `key` as a constevalutil::fixed_string usable as an NTTP.
template <const char *key>
consteval auto key_fixed_string() {
constexpr std::string_view key_view{ key };
char buffer[key_view.size() + 1] = {};
for (std::size_t i = 0; i < key_view.size(); ++i) { buffer[i] = key_view[i]; }
return constevalutil::fixed_string<key_view.size() + 1>(buffer);
}
// key_selector template arguments (one fixed_string per accepted key), in the
// order of accepted_keys.
template <typename T>
consteval std::vector<std::meta::info> selector_key_args() {
std::vector<std::meta::info> args;
template for (constexpr const char *key : std::define_static_array(accepted_keys(^^T))) {
args.push_back(std::meta::reflect_constant(key_fixed_string<key>()));
}
return args;
}
// key_selector whose keys are exactly T's accepted keys (index i <-> i-th key).
template <typename T>
using selector_for = typename [: std::meta::substitute(
^^rvv_vls::ondemand::key_selector, selector_key_args<T>()) :];
// True when T's accepted keys satisfy every key_selector requirement, so a
// key_selector can be built for T without a compile-time error. This mirrors the
// key_selector limits (see key_selector.h): at most 255 keys, each key non-empty
// and at most 63 characters, no backslash / double-quote / null byte, and all
// keys distinct. Keys with any other control character are excluded too: they
// are escaped in JSON, so their raw bytes never match. When this returns false
// the deserializer falls back to deserialize_struct_scan instead of failing to
// compile.
template <typename T>
consteval bool keys_fit_selector() {
std::vector<std::string_view> keys;
for (const char *key : accepted_keys(^^T)) { keys.push_back(key); }
if (keys.size() > 255) { return false; }
for (std::size_t i = 0; i < keys.size(); ++i) {
if (keys[i].empty() || keys[i].size() > 63) { return false; }
for (char c : keys[i]) {
if (c == '\\' || c == '"' || static_cast<unsigned char>(c) < 0x20) { return false; }
}
for (std::size_t j = i + 1; j < keys.size(); ++j) {
if (keys[i] == keys[j]) { return false; }
}
}
return true;
}
// True when the class `adapter` declares a member named deserialize.
consteval bool declares_deserialize(std::meta::info adapter) {
for (std::meta::info m : std::meta::members_of(adapter, std::meta::access_context::unchecked())) {
if (std::meta::has_identifier(m) && std::meta::identifier_of(m) == "deserialize") { return true; }
}
return false;
}
// Deserialize a JSON value into `target`, the storage of member `mem`, through
// the member's with<Adapter> annotation when the adapter provides a deserialize
// function.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member_value(ValueT &field_value, M &target) noexcept(false) {
constexpr std::meta::info with_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::with_t);
if constexpr (with_type != std::meta::info{}) {
using adapter = typename [: with_type :]::adapter;
using ondemand_value = rvv_vls::ondemand::value;
if constexpr (requires { { adapter::deserialize(field_value, target) } -> std::convertible_to<error_code>; }) {
return adapter::deserialize(field_value, target);
} else if constexpr (requires(ondemand_value &v) { { adapter::deserialize(v, target) } -> std::convertible_to<error_code>; }
&& requires { field_value.get_value(); }) {
// A transparent structure read from a document: the adapter takes an
// ondemand::value. A scalar document cannot be viewed as a value, so it
// reports SCALAR_DOCUMENT_AS_VALUE (an adapter taking auto& receives the
// document itself and has no such limitation).
ondemand_value v;
SIMDJSON_TRY(field_value.get_value().get(v));
return adapter::deserialize(v, target);
} else {
static_assert(!declares_deserialize(^^adapter),
"the deserialize function of a simdjson::with adapter must be callable as "
"Adapter::deserialize(simdjson::ondemand::value &, T &) and return an error_code");
return field_value.get(target);
}
} else {
return field_value.get(target);
}
}
// Deserialize the storage `target` of member `mem` from a JSON value.
template <auto mem, typename ValueT, typename M>
simdjson_warn_unused simdjson_inline error_code deserialize_member(ValueT &field_value, M &target) noexcept(false) {
if constexpr (has_default<mem>() && std::is_default_constructible_v<M> && std::is_move_assignable_v<M>) {
// A present key replaces the default value: deserialize into a fresh
// temporary so that, e.g., a container does not append to its default
// content, and a failure leaves the default untouched.
M value{};
SIMDJSON_TRY(deserialize_member_value<mem>(field_value, value));
target = std::move(value);
return SUCCESS;
} else {
return deserialize_member_value<mem>(field_value, target);
}
}
// Deserialize the field with the given field index.
template <typename T>
simdjson_warn_unused simdjson_inline error_code deserialize_field_at(
std::size_t field_index, rvv_vls::ondemand::value field_value, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (field_index == counter) { return deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
}
// Called when the key(s) of member `mem`, stored in `target`, are missing from
// the JSON object: an error for a required member, a call to the default_from
// factory, or nothing at all.
template <auto mem, typename M>
simdjson_warn_unused simdjson_inline error_code on_missing_member(M &target) noexcept(false) {
constexpr std::meta::info default_from_type = simdjson::detail::annotation_of_template(mem, ^^simdjson::detail::default_from_t);
if constexpr (default_from_type != std::meta::info{}) {
target = [: default_from_type :]::factory();
return SUCCESS;
} else if constexpr (may_be_absent<mem>()) {
// For optional and default_value members, a missing key is not an error:
// leave the member at its current (default) value.
(void)target;
return SUCCESS;
} else {
(void)target;
return NO_SUCH_FIELD;
}
}
// Report or handle every field that was not seen in the JSON object.
template <typename T, std::size_t N>
simdjson_warn_unused simdjson_inline error_code handle_missing_fields(
const std::array<bool, N> &seen_field, T &out) noexcept(false) {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
if (!seen_field[counter]) { SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out))); }
++counter;
}
return SUCCESS;
}
// Ordered, per-field deserialization: one obj[key] lookup per eligible field
// (and per alias, until one is found). This is the opt-out path
// (-DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1), except for structs with keys
// that need unescaping (see keys_need_unescaping).
template <typename T>
simdjson_warn_unused error_code deserialize_struct_ordered(
rvv_vls::ondemand::object &obj, T &out) noexcept(false) {
template for (constexpr auto path : std::define_static_array(eligible_fields(^^T))) {
using field = [: path :];
rvv_vls::ondemand::value field_value;
error_code error = NO_SUCH_FIELD;
template for (constexpr const char *key : std::define_static_array(accepted_keys_of(field::leaf))) {
if (error == NO_SUCH_FIELD) { error = obj[std::string_view(key)].get(field_value); }
}
if (error == NO_SUCH_FIELD) {
SIMDJSON_TRY(on_missing_member<field::leaf>(field::get(out)));
} else if (error) {
return error;
} else {
SIMDJSON_TRY(deserialize_member<field::leaf>(field_value, field::get(out)));
}
}
return SUCCESS;
}
// Appends the JSON keys that serialization writes for members of `type` that
// deserialization cannot assign (const or non-public members, directly or
// through flatten). `all` is true inside a flattened member that is itself
// unassignable. Keys of skip_deserializing members are not included: they are
// unknown keys, as documented.
consteval void append_unassignable_keys(std::meta::info type, bool all, std::vector<const char *> &keys) {
for (std::meta::info mem : std::meta::nonstatic_data_members_of(type, std::meta::access_context::unchecked())) {
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_serializing_tag)
|| simdjson::detail::has_annotation(mem, ^^simdjson::detail::skip_deserializing_tag)) {
continue;
}
bool unassignable = all || !is_eligible_member(mem);
if (simdjson::detail::has_annotation(mem, ^^simdjson::detail::flatten_tag)) {
append_unassignable_keys(simdjson::detail::flattened_type(mem), unassignable, keys);
} else if (unassignable) {
keys.push_back(std::define_static_string(simdjson::detail::json_key_name(mem)));
}
}
}
consteval std::vector<const char *> unassignable_keys(std::meta::info type) {
std::vector<const char *> keys;
append_unassignable_keys(type, false, keys);
return keys;
}
// Deserialization by a single pass over every field of the object, comparing
// unescaped keys. It is used for structs annotated with deny_unknown_fields
// (DenyUnknown = true), where a key that does not map to an eligible field is
// reported as UNKNOWN_FIELD, and as the fallback for structs whose keys the
// key_selector or obj[key] cannot match (see keys_fit_selector and
// keys_need_unescaping). As with object::for_each, the first occurrence of a
// field wins (later duplicates, or aliases of a field already seen, are ignored).
template <bool DenyUnknown, typename T>
simdjson_warn_unused error_code deserialize_struct_scan(
rvv_vls::ondemand::object &obj, T &out) noexcept(false) {
static constexpr auto keys = std::define_static_array(accepted_keys(^^T));
static constexpr auto key_fields = std::define_static_array(accepted_key_fields(^^T));
std::array<bool, eligible_field_count<T>()> seen_field{};
for (auto field_result : obj) {
rvv_vls::ondemand::field json_field;
SIMDJSON_TRY(std::move(field_result).get(json_field));
std::string_view key;
SIMDJSON_TRY(json_field.unescaped_key().get(key));
std::size_t key_index = keys.size();
for (std::size_t i = 0; i < keys.size(); ++i) {
if (key == std::string_view(keys[i])) { key_index = i; break; }
}
if (key_index == keys.size()) {
if constexpr (DenyUnknown) {
// A key that T itself serializes (e.g. of a const member) is not
// unknown: a serialized value must parse back.
static constexpr auto ignored_keys = std::define_static_array(unassignable_keys(^^T));
bool ignored = false;
for (const char *ignored_key : ignored_keys) {
if (key == std::string_view(ignored_key)) { ignored = true; break; }
}
if (!ignored) { return UNKNOWN_FIELD; }
}
continue;
}
const std::size_t field_index = key_fields[key_index];
if (seen_field[field_index]) { continue; }
seen_field[field_index] = true;
SIMDJSON_TRY(deserialize_field_at(field_index, json_field.value(), out));
}
return handle_missing_fields(seen_field, out);
}
template <typename T>
consteval bool is_transparent() {
return simdjson::detail::has_annotation(^^T, ^^simdjson::detail::transparent_tag);
}
} // namespace key_selector_reflection_detail
// Deserialize a reflected struct. By default this builds a compile-time
// key_selector from the struct's members and walks each object once with
// object::for_each (perfect-hash key matching), instead of one obj[key] lookup
// per member. Other paths are used instead:
// - globally, the ordered per-member path when defining
// -DSIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION=1;
// - automatically and per-type, a scan of the object comparing unescaped keys
// when the struct's keys do not fit the key_selector limits (see
// keys_fit_selector) or cannot be compared raw (see keys_need_unescaping),
// so that long member names and the like keep compiling rather than
// tripping a static_assert.
// Structs annotated with deny_unknown_fields always use the strict scan, and
// structs annotated with transparent are deserialized as their single member.
//
// noexcept(false): a member's tag_invoke may throw and the exception must reach
// the caller of get<T>().
template <typename T, typename ValT>
requires(user_defined_type<T> && std::is_class_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) {
if constexpr (key_selector_reflection_detail::is_transparent<T>()) {
constexpr auto mem = simdjson::detail::transparent_member(^^T);
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, rvv_vls::ondemand::object>) {
// We were handed an object: only a structure can be deserialized from it.
if constexpr (user_defined_type<typename [: std::meta::type_of(mem) :]>) {
return tag_invoke(deserialize_tag{}, val, out.[:mem:]);
} else {
return INCORRECT_TYPE;
}
} else {
return key_selector_reflection_detail::deserialize_member<mem>(val, out.[:mem:]);
}
} else {
static_assert(key_selector_reflection_detail::accepted_keys_are_distinct<T>(),
"two members of this structure accept the same JSON key (check rename, alias, "
"rename_all and flatten)");
rvv_vls::ondemand::object obj;
if constexpr (std::is_same_v<std::remove_cvref_t<ValT>, rvv_vls::ondemand::object>) {
obj = val;
} else {
SIMDJSON_TRY(val.get_object().get(obj));
}
if constexpr (simdjson::detail::has_annotation(^^T, ^^simdjson::detail::deny_unknown_fields_tag)) {
return key_selector_reflection_detail::deserialize_struct_scan<true>(obj, out);
} else {
#if defined(SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION) && SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
// Opt-out build: use the ordered per-member path, unless obj[key] cannot
// match T's keys.
if constexpr (key_selector_reflection_detail::keys_need_unescaping<T>()) {
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
return key_selector_reflection_detail::deserialize_struct_ordered(obj, out);
}
#else
if constexpr (key_selector_reflection_detail::eligible_field_count<T>() == 0) {
// No fields to deserialize: an empty key_selector cannot be built, so just
// validate that the input is an object (done above) and succeed. Mirrors the
// ordered per-member path, which iterates over zero members.
(void)out;
(void)obj;
return SUCCESS;
} else if constexpr (!key_selector_reflection_detail::keys_fit_selector<T>()) {
// Automatic fallback: T's accepted keys do not fit the key_selector limits
// (e.g. a member name longer than 63 characters, or a key with a double
// quote), so building a selector would be a compile error. Scan the object
// instead, so the default never breaks a struct that the opt-out path would
// accept.
return key_selector_reflection_detail::deserialize_struct_scan<false>(obj, out);
} else {
using selector = key_selector_reflection_detail::selector_for<T>;
if constexpr (key_selector_reflection_detail::all_eligible_fields_required<T>()
&& !key_selector_reflection_detail::has_aliases<T>()) {
// Fast path: every member is required and has a single key. A single
// for_each pass parses each matched field; the returned match count then
// tells us whether every member was present (matched_count ==
// selector::size()) without a per-member "seen" array. A value-parse error
// (e.g. a type mismatch) is propagated by for_each.
auto walk = obj.template for_each<selector>(
[&](std::size_t matched_index, rvv_vls::ondemand::value field_value) -> error_code {
std::size_t counter = 0;
template for (constexpr auto path : std::define_static_array(key_selector_reflection_detail::eligible_fields(^^T))) {
using field = [: path :];
if (matched_index == counter) { return key_selector_reflection_detail::deserialize_member<field::leaf>(field_value, field::get(out)); }
++counter;
}
return SUCCESS;
});
if (walk.error) { return walk.error; }
// A missing required member shows up as a short match count and is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path.
if (walk.matched_count != selector::size()) { return NO_SUCH_FIELD; }
return SUCCESS;
} else {
static constexpr auto key_fields = std::define_static_array(key_selector_reflection_detail::accepted_key_fields(^^T));
std::array<bool, key_selector_reflection_detail::eligible_field_count<T>()> seen_field{};
// Single pass over the object: each field whose key matches a member (or one
// of its aliases) yields its selector index, which we map back to the
// corresponding member. The first key seen for a member wins. The callback
// returns an error_code so that a value-parse error (e.g. a type mismatch on
// a matched field) is propagated by for_each instead of being silently dropped.
error_code walk_error = obj.template for_each<selector>(
[&](std::size_t matched_index, rvv_vls::ondemand::value field_value) -> error_code {
const std::size_t field_index = key_fields[matched_index];
if (seen_field[field_index]) { return SUCCESS; }
seen_field[field_index] = true;
return key_selector_reflection_detail::deserialize_field_at(field_index, field_value, out);
});
if (walk_error) { return walk_error; }
// Required members must be present: a missing one is reported as
// NO_SUCH_FIELD, mirroring the ordered obj[key] path. Optional and defaulted
// members may be absent.
return key_selector_reflection_detail::handle_missing_fields(seen_field, out);
}
}
#endif // SIMDJSON_DISABLE_KEY_SELECTOR_REFLECTION
}
}
}
// Support for enum deserialization - deserialize from string representation using
// expand approach from P2996R12. The accepted strings are the enumerator's JSON key
// (see rename and rename_all) and its aliases.
template <typename T, typename ValT>
requires(std::is_enum_v<T>)
error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept {
#if SIMDJSON_STATIC_REFLECTION
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
static constexpr auto enumerators = std::define_static_array(std::meta::enumerators_of(^^T));
template for (constexpr auto enum_val : enumerators) {
template for (constexpr const char *key : std::define_static_array(key_selector_reflection_detail::accepted_keys_of(enum_val))) {
if (str == std::string_view(key)) {
out = [:enum_val:];
return SUCCESS;
}
}
};
return INCORRECT_TYPE;
#else
// Fallback: deserialize as integer if reflection not available
std::underlying_type_t<T> int_val;
SIMDJSON_TRY(val.get(int_val));
out = static_cast<T>(int_val);
return SUCCESS;
#endif
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::unique_ptr<T> &out) noexcept(false) {
std::unique_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
template <typename simdjson_value, typename T>
requires(user_defined_type<std::remove_cvref_t<T>>)
error_code tag_invoke(deserialize_tag, simdjson_value &val, std::shared_ptr<T> &out) noexcept(false) {
std::shared_ptr<T> ptr(new (std::nothrow) T());
if (!ptr) {
return MEMALLOC;
}
SIMDJSON_TRY(val.get(*ptr));
out = std::move(ptr);
return SUCCESS;
}
#endif // SIMDJSON_STATIC_REFLECTION
////////////////////////////////////////
// Unique pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Shared pointers
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<bool> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<bool>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_bool().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<int64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<int64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_int64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<uint64_t> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<uint64_t>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_uint64().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<double> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<double>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_double().get(*out));
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string_view> &out) noexcept {
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset();
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string_view>();
if (!out) { return MEMALLOC; }
}
SIMDJSON_TRY(val.get_string().get(*out));
return SUCCESS;
}
////////////////////////////////////////
// Explicit optional specializations
////////////////////////////////////////
////////////////////////////////////////
// Explicit smart pointer specializations for string and int types
////////////////////////////////////////
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::shared_ptr<std::string> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_shared<std::string>();
}
std::string_view str;
SIMDJSON_TRY(val.get_string().get(str));
*out = std::string{str};
return SUCCESS;
}
error_code tag_invoke(deserialize_tag, auto &val, std::unique_ptr<int> &out) noexcept {
// Check if the value is null
bool is_null_value;
SIMDJSON_TRY( val.is_null().get(is_null_value) );
if (is_null_value) {
out.reset(); // Set to nullptr
return SUCCESS;
}
if (!out) {
out = std::make_unique<int>();
}
int64_t temp;
SIMDJSON_TRY(val.get_int64().get(temp));
*out = static_cast<int>(temp);
return SUCCESS;
}
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H
#endif // SIMDJSON_SUPPORTS_CONCEPTS
/* end file simdjson/generic/ondemand/std_deserialize.h for rvv_vls */
// Inline definitions
/* including simdjson/generic/ondemand/array-inl.h for rvv_vls: #include "simdjson/generic/ondemand/array-inl.h" */
/* begin file simdjson/generic/ondemand/array-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
//
// ### Live States
//
// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the array is first found and the iterator is just past the `{`.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
// depth == iter->depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter->depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the array iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter->depth == depth, and at_start == false.
//
// ## Terminal State
//
// The terminal state has iter->depth < depth. at_start is always false.
//
// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
// by decrementing depth. In this state, iter->depth < depth, at_start == false, and
// error == SUCCESS.
//
simdjson_inline array::array(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
// We don't need to know if the array is empty to start iteration, but we do want to know if there
// is an error--thus `simdjson_unused`.
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
simdjson_unused bool has_value;
SIMDJSON_TRY( iter.start_root_array().get(has_value) );
return array(iter);
}
simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
bool has_value;
SIMDJSON_TRY(iter.started_array().get(has_value));
return array(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return array_iterator(iter, this);
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The array is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return array_iterator(iter);
}
simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
return array_iterator(iter);
}
simdjson_warn_unused simdjson_warn_unused simdjson_inline error_code array::consume() noexcept {
auto error = iter.json_iter().skip_child(iter.depth()-1);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept {
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the array after counting the number of elements.
iter.reset_array();
return count;
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_array().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
inline simdjson_result<bool> array::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_array();
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void array::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
// - means "the append position" or "the element after the end of the array"
// We don't support this, because we're returning a real element, not a position.
if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
size_t array_index = 0;
size_t i;
SIMDJSON_TRY(internal::parse_json_pointer_array_index(json_pointer, array_index, i));
// Get the child
auto child = at(array_index);
// If there is an error, it ends here
if(child.error()) {
return child;
}
// If there is a /, we're not done yet, call recursively.
if (i < json_pointer.length()) {
child = child.at_pointer(json_pointer.substr(i));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code array::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Without this check, an empty key is treated as index
// 0 and remaining_path may be unchanged, causing infinite recursion.
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Wildcard case
if (key=="*"){
for(auto element: *this) {
value val;
SIMDJSON_TRY(element.get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// An element that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
// Specific index case in which we access the element at the given index
for (char c : key) {
if(c < '0' || c > '9'){
return INVALID_JSON_POINTER;
}
}
// Rejects leading zeros and indexes that overflow size_t, as DOM does.
size_t idx;
size_t idx_length;
error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length);
value val;
if (!error) { error = at(idx).get(val); }
if (error) {
if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()){
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
size_t i = 0;
for (auto value : *this) {
if (i == index) { return value; }
i++;
}
return INDEX_OUT_OF_BOUNDS;
}
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::array>::simdjson_result(
rvv_vls::ondemand::array &&value
) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::array>(
std::forward<rvv_vls::ondemand::array>(value)
)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array>::simdjson_result(
error_code error
) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::array>(error)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::array>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::array>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::array>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<size_t> simdjson_result<rvv_vls::ondemand::array>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::array>::is_empty() & noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::array>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::array>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<rvv_vls::ondemand::array>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::array>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
/* end file simdjson/generic/ondemand/array-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/array_iterator-inl.h for rvv_vls: #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/array_iterator-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline array_iterator::array_iterator(const value_iterator &_iter, array* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
simdjson_inline array_iterator::~array_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline array_iterator::array_iterator(array_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline array_iterator& array_iterator::operator=(array_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline array_iterator::array_iterator(const array_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline array_iterator& array_iterator::operator=(const array_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<value> array_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
if (iter.error()) { iter.abandon(); return iter.error(); }
return value(iter.child());
}
simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept {
return iter.is_open();
}
simdjson_inline array_iterator &array_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
has_been_referenced = false;
#endif
error_code error;
// PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here.
// However, it does not seem to make a perf difference, so we add it out of an abundance of caution.
if (( error = iter.error() )) { return *this; }
if (( error = iter.skip_child() )) { return *this; }
if (( error = iter.has_next_element().error() )) { return *this; }
return *this;
}
simdjson_inline bool array_iterator::at_end() const noexcept {
return iter.at_end();
}
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator>::simdjson_result(
rvv_vls::ondemand::array_iterator &&value
) noexcept
: rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::array_iterator>(std::forward<rvv_vls::ondemand::array_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator>::simdjson_result(error_code error) noexcept
: rvv_vls::implementation_simdjson_result_base<rvv_vls::ondemand::array_iterator>({}, error)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::array_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
simdjson_inline bool simdjson_result<rvv_vls::ondemand::array_iterator>::operator==(const simdjson_result<rvv_vls::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
simdjson_inline bool simdjson_result<rvv_vls::ondemand::array_iterator>::operator!=(const simdjson_result<rvv_vls::ondemand::array_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> &simdjson_result<rvv_vls::ondemand::array_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++(first);
return *this;
}
simdjson_inline bool simdjson_result<rvv_vls::ondemand::array_iterator>::at_end() const noexcept {
return !first.iter.is_valid() || first.at_end();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/array_iterator-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/value-inl.h for rvv_vls: #include "simdjson/generic/ondemand/value-inl.h" */
/* begin file simdjson/generic/ondemand/value-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
/**
* @private Narrows the result of get_uint64() to a smaller unsigned integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<uint64_t> wide) noexcept {
uint64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<uint64_t>((std::numeric_limits<T>::max)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
/**
* @private Narrows the result of get_int64() to a smaller signed integer type T.
* @returns NUMBER_OUT_OF_RANGE if the value does not fit in a T.
*/
template <typename T>
simdjson_inline simdjson_result<T> narrow_integer(simdjson_result<int64_t> wide) noexcept {
int64_t result;
SIMDJSON_TRY(std::move(wide).get(result));
if (result > static_cast<int64_t>((std::numeric_limits<T>::max)()) || result < static_cast<int64_t>((std::numeric_limits<T>::min)())) { return NUMBER_OUT_OF_RANGE; }
return static_cast<T>(result);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
// get_float32() parses with get_float(), so float must be binary32.
static_assert(std::numeric_limits<float>::is_iec559 && std::numeric_limits<float>::digits == 24,
"get_float32() requires float to be IEEE binary32");
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
// get_float64() parses with get_double(), so double must be binary64.
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
"get_float64() requires double to be IEEE binary64");
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline value::value(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline value value::start(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline value value::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline simdjson_result<array> value::get_array() noexcept {
return array::start(iter);
}
simdjson_inline simdjson_result<object> value::get_object() noexcept {
return object::start(iter);
}
simdjson_inline simdjson_result<object> value::start_or_resume_object() noexcept {
if (iter.at_start()) {
return get_object();
} else {
return object::resume(iter);
}
}
simdjson_inline simdjson_result<raw_json_string> value::get_raw_json_string() noexcept {
return iter.get_raw_json_string();
}
simdjson_inline simdjson_result<std::string_view> value::get_string(bool allow_replacement) noexcept {
return iter.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> value::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept {
return iter.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> value::get_wobbly_string() noexcept {
return iter.get_wobbly_string();
}
simdjson_inline simdjson_result<double> value::get_double() noexcept {
return iter.get_double();
}
simdjson_inline simdjson_result<double> value::get_double_in_string() noexcept {
return iter.get_double_in_string();
}
simdjson_inline simdjson_result<float> value::get_float() noexcept {
return iter.get_float();
}
simdjson_inline simdjson_result<float> value::get_float_in_string() noexcept {
return iter.get_float_in_string();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64() noexcept {
return iter.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> value::get_uint64_in_string() noexcept {
return iter.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> value::get_int64() noexcept {
return iter.get_int64();
}
simdjson_inline simdjson_result<int64_t> value::get_int64_in_string() noexcept {
return iter.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> value::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> value::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> value::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> value::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> value::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> value::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> value::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> value::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<bool> value::get_bool() noexcept {
return iter.get_bool();
}
simdjson_inline simdjson_result<bool> value::is_null() noexcept {
return iter.is_null();
}
template<> simdjson_inline simdjson_result<array> value::get() noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> value::get() noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> value::get() noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> value::get() noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> value::get() noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<number> value::get() noexcept { return get_number(); }
template<> simdjson_inline simdjson_result<double> value::get() noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> value::get() noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> value::get() noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> value::get() noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> value::get() noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> value::get() noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> value::get() noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> value::get() noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> value::get() noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> value::get() noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> value::get() noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> value::get() noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> value::get() noexcept { return get_bool(); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::u8string_view& out) noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(float& out) noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint32_t& out) noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int32_t& out) noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint16_t& out) noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int16_t& out) noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(uint8_t& out) noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code value::get(int8_t& out) noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float32_t& out) noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(std::float64_t& out) noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline value::operator T() noexcept(false) {
return get<T>();
}
simdjson_inline value::operator array() noexcept(false) {
return get_array();
}
simdjson_inline value::operator object() noexcept(false) {
return get_object();
}
simdjson_inline value::operator uint64_t() noexcept(false) {
return get_uint64();
}
simdjson_inline value::operator int64_t() noexcept(false) {
return get_int64();
}
simdjson_inline value::operator double() noexcept(false) {
return get_double();
}
simdjson_inline value::operator std::string_view() noexcept(false) {
return get_string(false);
}
simdjson_inline value::operator raw_json_string() noexcept(false) {
return get_raw_json_string();
}
simdjson_inline value::operator bool() noexcept(false) {
return get_bool();
}
#endif
simdjson_inline simdjson_result<array_iterator> value::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> value::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<size_t> value::count_elements() & noexcept {
simdjson_result<size_t> answer;
auto a = get_array();
answer = a.count_elements();
// count_elements leaves you pointing inside the array, at the first element.
// We need to move back so that the user can create a new array (which requires that
// we point at '[').
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<size_t> value::count_fields() & noexcept {
simdjson_result<size_t> answer;
auto a = get_object();
answer = a.count_fields();
iter.move_at_start();
return answer;
}
simdjson_inline simdjson_result<value> value::at(size_t index) noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<value> value::find_field(std::string_view key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field(const char *key) noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(std::string_view key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::find_field_unordered(const char *key) noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> value::operator[](std::string_view key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> value::operator[](const char *key) noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<json_type> value::type() noexcept {
return iter.type();
}
simdjson_inline simdjson_result<bool> value::is_scalar() noexcept {
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> value::is_integer() noexcept {
return iter.is_integer();
}
simdjson_warn_unused simdjson_inline simdjson_result<number_type> value::get_number_type() noexcept {
return iter.get_number_type();
}
simdjson_warn_unused simdjson_inline simdjson_result<number> value::get_number() noexcept {
return iter.get_number();
}
simdjson_inline std::string_view value::raw_json_token() noexcept {
return std::string_view(reinterpret_cast<const char*>(iter.peek_start()), iter.peek_start_length());
}
simdjson_inline simdjson_result<std::string_view> value::raw_json() noexcept {
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array: {
ondemand::array array;
SIMDJSON_TRY(get_array().get(array));
return array.raw_json();
}
case json_type::object: {
ondemand::object object;
SIMDJSON_TRY(get_object().get(object));
return object.raw_json();
}
default:
return raw_json_token();
}
}
simdjson_inline simdjson_result<const char *> value::current_location() noexcept {
return iter.json_iter().current_location();
}
simdjson_inline int32_t value::current_depth() const noexcept{
return iter.json_iter().depth();
}
inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept {
if (simdjson_unlikely(json_pointer.empty())) { // can't be
return false;
}
if (simdjson_unlikely(json_pointer[0] != '/')) {
return false;
}
size_t escape = json_pointer.find('~');
if (escape == std::string_view::npos) {
return true;
}
if (escape == json_pointer.size() - 1) {
return false;
}
if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') {
return false;
}
return true;
}
simdjson_inline simdjson_result<value> value::at_pointer(std::string_view json_pointer) noexcept {
// The empty JSON Pointer refers to the whole value (RFC 6901), as in
// document::at_pointer.
if (json_pointer.empty()) { return value(iter); }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
// a non-empty string can be invalid, or accessing a primitive (issue 2154)
if (is_pointer_well_formed(json_pointer)) {
return NO_SUCH_FIELD;
}
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code value::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
// Every recursive step of for_each_at_path_with_wildcard goes through this
// function, and each one descends one level into the document. A path with
// many segments applied to a deeply nested document would otherwise recurse
// without bound and overflow the stack.
if (size_t(iter.depth()) >= iter.json_iter().parser->max_depth()) { return DEPTH_ERROR; }
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::simdjson_result(
rvv_vls::ondemand::value &&value
) noexcept :
implementation_simdjson_result_base<rvv_vls::ondemand::value>(
std::forward<rvv_vls::ondemand::value>(value)
)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<rvv_vls::ondemand::value>(error)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<rvv_vls::ondemand::value>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<rvv_vls::ondemand::value>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::at(size_t index) noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::value>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::value>::end() & noexcept {
if (error()) { return error(); }
return {};
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::find_field(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::find_field(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::find_field_unordered(std::string_view key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::find_field_unordered(const char *key) noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::operator[](std::string_view key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::operator[](const char *key) noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array> simdjson_result<rvv_vls::ondemand::value>::get_array() noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::object> simdjson_result<rvv_vls::ondemand::value>::get_object() noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<rvv_vls::ondemand::value>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<rvv_vls::ondemand::value>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<rvv_vls::ondemand::value>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<rvv_vls::ondemand::value>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<rvv_vls::ondemand::value>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<rvv_vls::ondemand::value>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<rvv_vls::ondemand::value>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<rvv_vls::ondemand::value>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<rvv_vls::ondemand::value>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<rvv_vls::ondemand::value>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<rvv_vls::ondemand::value>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<float> simdjson_result<rvv_vls::ondemand::value>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<rvv_vls::ondemand::value>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<rvv_vls::ondemand::value>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<rvv_vls::ondemand::value>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<double> simdjson_result<rvv_vls::ondemand::value>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::value>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<rvv_vls::ondemand::value>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline error_code simdjson_result<rvv_vls::ondemand::value>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::value>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::raw_json_string> simdjson_result<rvv_vls::ondemand::value>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::value>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::value>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<> simdjson_inline error_code simdjson_result<rvv_vls::ondemand::value>::get<rvv_vls::ondemand::value>(rvv_vls::ondemand::value &out) noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template<typename T> simdjson_inline simdjson_result<T> simdjson_result<rvv_vls::ondemand::value>::get()
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T> simdjson_inline error_code simdjson_result<rvv_vls::ondemand::value>::get(T &out)
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::value>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<> simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::get<rvv_vls::ondemand::value>() noexcept {
if (error()) { return error(); }
return std::move(first);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::json_type> simdjson_result<rvv_vls::ondemand::value>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::value>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::value>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::value>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::value>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<rvv_vls::number_type> simdjson_result<rvv_vls::ondemand::value>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::number> simdjson_result<rvv_vls::ondemand::value>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator rvv_vls::ondemand::array() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator rvv_vls::ondemand::object() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator rvv_vls::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::value>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::value>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
simdjson_inline simdjson_result<const char *> simdjson_result<rvv_vls::ondemand::value>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<rvv_vls::ondemand::value>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::at_pointer(
std::string_view json_pointer) noexcept {
if (error()) {
return error();
}
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::value>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
inline error_code simdjson_result<rvv_vls::ondemand::value>::for_each_at_path_with_wildcard(
std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H
/* end file simdjson/generic/ondemand/value-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/document-inl.h for rvv_vls: #include "simdjson/generic/ondemand/document-inl.h" */
/* begin file simdjson/generic/ondemand/document-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* 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_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <limits>
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept
: iter{std::forward<json_iterator>(_iter)}
{
logger::log_start_value(iter, "document");
}
simdjson_inline document document::start(json_iterator &&iter) noexcept {
return document(std::forward<json_iterator>(iter));
}
inline void document::rewind() noexcept {
iter.rewind();
}
inline std::string document::to_debug_string() noexcept {
return iter.to_string();
}
inline simdjson_result<const char *> document::current_location() const noexcept {
return iter.current_location();
}
inline int32_t document::current_depth() const noexcept {
return iter.depth();
}
inline bool document::at_end() const noexcept {
return iter.at_end();
}
inline bool document::is_alive() noexcept {
return iter.is_alive();
}
simdjson_inline value_iterator document::resume_value_iterator() noexcept {
return value_iterator(&iter, 1, iter.root_position());
}
simdjson_inline value_iterator document::get_root_value_iterator() noexcept {
return resume_value_iterator();
}
simdjson_inline simdjson_result<object> document::start_or_resume_object() noexcept {
if (iter.at_root()) {
return get_object();
} else {
return object::resume(resume_value_iterator());
}
}
simdjson_inline simdjson_result<value> document::get_value() noexcept {
// Make sure we start any arrays or objects before returning, so that start_root_<object/array>()
// gets called.
// It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether
// we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error.
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; }
#endif
// assert_at_root() serves two purposes: in Debug mode, whether or not
// SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of
// the document (this will typically be redundant). In release mode, it generates
// SIMDJSON_ASSUME statements to allow the compiler to make assumptions.
iter.assert_at_root();
switch (*iter.peek()) {
case '[': {
// The following lines check that the document ends with ].
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_array();
if(error) { return error; }
return value(get_root_value_iterator());
}
case '{': {
// The following lines would check that the document ends with }.
auto value_iterator = get_root_value_iterator();
auto error = value_iterator.check_root_object();
if(error) { return error; }
return value(get_root_value_iterator());
}
default:
// Unfortunately, scalar documents are a special case in simdjson and they cannot
// be safely converted to value instances.
return SCALAR_DOCUMENT_AS_VALUE;
}
}
simdjson_inline simdjson_result<array> document::get_array() & noexcept {
auto value = get_root_value_iterator();
return array::start_root(value);
}
simdjson_inline simdjson_result<object> document::get_object() & noexcept {
auto value = get_root_value_iterator();
return object::start_root(value);
}
/**
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content. We want to disallow trailing
* content.
* Thus, in several implementations below, we pass a 'true' parameter value to
* a get_root_value_iterator() method: this indicates that we disallow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document::get_uint64() noexcept {
return get_root_value_iterator().get_root_uint64(true);
}
simdjson_inline simdjson_result<uint64_t> document::get_uint64_in_string() noexcept {
return get_root_value_iterator().get_root_uint64_in_string(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64() noexcept {
return get_root_value_iterator().get_root_int64(true);
}
simdjson_inline simdjson_result<int64_t> document::get_int64_in_string() noexcept {
return get_root_value_iterator().get_root_int64_in_string(true);
}
simdjson_inline simdjson_result<uint32_t> document::get_uint32() noexcept {
return narrow_integer<uint32_t>(get_uint64());
}
simdjson_inline simdjson_result<int32_t> document::get_int32() noexcept {
return narrow_integer<int32_t>(get_int64());
}
simdjson_inline simdjson_result<uint16_t> document::get_uint16() noexcept {
return narrow_integer<uint16_t>(get_uint64());
}
simdjson_inline simdjson_result<int16_t> document::get_int16() noexcept {
return narrow_integer<int16_t>(get_int64());
}
simdjson_inline simdjson_result<uint8_t> document::get_uint8() noexcept {
return narrow_integer<uint8_t>(get_uint64());
}
simdjson_inline simdjson_result<int8_t> document::get_int8() noexcept {
return narrow_integer<int8_t>(get_int64());
}
simdjson_inline simdjson_result<double> document::get_double() noexcept {
return get_root_value_iterator().get_root_double(true);
}
simdjson_inline simdjson_result<double> document::get_double_in_string() noexcept {
return get_root_value_iterator().get_root_double_in_string(true);
}
simdjson_inline simdjson_result<float> document::get_float() noexcept {
return get_root_value_iterator().get_root_float(true);
}
simdjson_inline simdjson_result<float> document::get_float_in_string() noexcept {
return get_root_value_iterator().get_root_float_in_string(true);
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document::get_string(bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(true, allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept {
return get_root_value_iterator().get_root_string(receiver, true, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> document::get_wobbly_string() noexcept {
return get_root_value_iterator().get_root_wobbly_string(true);
}
simdjson_inline simdjson_result<raw_json_string> document::get_raw_json_string() noexcept {
return get_root_value_iterator().get_root_raw_json_string(true);
}
simdjson_inline simdjson_result<bool> document::get_bool() noexcept {
return get_root_value_iterator().get_root_bool(true);
}
simdjson_inline simdjson_result<bool> document::is_null() noexcept {
return get_root_value_iterator().is_root_null(true);
}
template<> simdjson_inline simdjson_result<array> document::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document::get() & noexcept { return get_value(); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::u8string_view& out) & noexcept { return get_u8string(false).get(out); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(float& out) & noexcept { return get_float().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint32_t& out) & noexcept { return get_uint32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int32_t& out) & noexcept { return get_int32().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint16_t& out) & noexcept { return get_uint16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int16_t& out) & noexcept { return get_int16().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(uint8_t& out) & noexcept { return get_uint8().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(int8_t& out) & noexcept { return get_int8().get(out); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float32_t& out) & noexcept { return get_float32().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(std::float64_t& out) & noexcept { return get_float64().get(out); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_warn_unused simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); }
template<> simdjson_warn_unused simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); }
template<> simdjson_deprecated simdjson_inline simdjson_result<raw_json_string> document::get() && noexcept { return get_raw_json_string(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<std::string_view> document::get() && noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<std::u8string_view> document::get() && noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_deprecated simdjson_inline simdjson_result<double> document::get() && noexcept { return std::forward<document>(*this).get_double(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<float> document::get() && noexcept { return std::forward<document>(*this).get_float(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<uint64_t> document::get() && noexcept { return std::forward<document>(*this).get_uint64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<int64_t> document::get() && noexcept { return std::forward<document>(*this).get_int64(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<bool> document::get() && noexcept { return std::forward<document>(*this).get_bool(); }
template<> simdjson_deprecated simdjson_inline simdjson_result<value> document::get() && noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get<T>(); }
template <class T>
simdjson_inline document::operator T() & noexcept(false) { return get<T>(); }
simdjson_inline document::operator array() & noexcept(false) { return get_array(); }
simdjson_inline document::operator object() & noexcept(false) { return get_object(); }
simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document::operator double() noexcept(false) { return get_double(); }
simdjson_inline document::operator std::string_view() noexcept(false) simdjson_lifetime_bound { return get_string(false); }
simdjson_inline document::operator raw_json_string() noexcept(false) simdjson_lifetime_bound { return get_raw_json_string(); }
simdjson_inline document::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document::operator value() noexcept(false) { return get_value(); }
#endif
simdjson_inline simdjson_result<size_t> document::count_elements() & noexcept {
auto a = get_array();
simdjson_result<size_t> answer = a.count_elements();
/* If there was an array, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<size_t> document::count_fields() & noexcept {
auto a = get_object();
simdjson_result<size_t> answer = a.count_fields();
/* If there was an object, we are now left pointing at its first element. */
if(answer.error() == SUCCESS) { rewind(); }
return answer;
}
simdjson_inline simdjson_result<value> document::at(size_t index) & noexcept {
auto a = get_array();
return a.at(index);
}
simdjson_inline simdjson_result<array_iterator> document::begin() & noexcept {
return get_array().begin();
}
simdjson_inline simdjson_result<array_iterator> document::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<value> document::find_field(std::string_view key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field(const char *key) & noexcept {
return start_or_resume_object().find_field(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(std::string_view key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::find_field_unordered(const char *key) & noexcept {
return start_or_resume_object().find_field_unordered(key);
}
simdjson_inline simdjson_result<value> document::operator[](std::string_view key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_inline simdjson_result<value> document::operator[](const char *key) & noexcept {
return start_or_resume_object()[key];
}
simdjson_warn_unused simdjson_inline error_code document::consume() noexcept {
bool scalar = false;
auto error = is_scalar().get(scalar);
if(error) { return error; }
if(scalar) {
iter.return_current_and_advance();
return SUCCESS;
}
error = iter.skip_child(0);
if(error) { iter.abandon(); }
return error;
}
simdjson_inline simdjson_result<std::string_view> document::raw_json() noexcept {
auto _iter = get_root_value_iterator();
const uint8_t * starting_point{_iter.peek_start()};
auto error = consume();
if(error) { return error; }
// After 'consume()', we could be left pointing just beyond the document, but that
// is ok because we are not going to dereference the final pointer position, we just
// use it to compute the length in bytes.
const uint8_t * final_point{iter.unsafe_pointer()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<json_type> document::type() noexcept {
return get_root_value_iterator().type();
}
simdjson_inline simdjson_result<bool> document::is_scalar() noexcept {
// For more speed, we could do:
// return iter.is_single_token();
json_type this_type;
auto error = type().get(this_type);
if(error) { return error; }
return ! ((this_type == json_type::array) || (this_type == json_type::object));
}
simdjson_inline simdjson_result<bool> 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();
}
simdjson_inline simdjson_result<bool> document::is_integer() noexcept {
return get_root_value_iterator().is_root_integer(true);
}
simdjson_inline simdjson_result<number_type> document::get_number_type() noexcept {
return get_root_value_iterator().get_root_number_type(true);
}
simdjson_inline simdjson_result<number> document::get_number() noexcept {
return get_root_value_iterator().get_root_number(true);
}
simdjson_inline simdjson_result<std::string_view> document::raw_json_token() noexcept {
auto _iter = get_root_value_iterator();
return std::string_view(reinterpret_cast<const char*>(_iter.peek_start()), _iter.peek_root_length());
}
simdjson_inline simdjson_result<value> document::at_pointer(std::string_view json_pointer) noexcept {
rewind(); // Rewind the document each time at_pointer is called
if (json_pointer.empty()) {
return this->get_value();
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t)
{
case json_type::array:
return (*this).get_array().at_pointer(json_pointer);
case json_type::object:
return (*this).get_object().at_pointer(json_pointer);
default:
return INVALID_JSON_POINTER;
}
}
simdjson_inline simdjson_result<value> 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;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
rewind(); // Rewind the document each time for_each_at_path_with_wildcard is called
if (json_path.empty()) {
return INVALID_JSON_POINTER;
}
json_type t;
SIMDJSON_TRY(type().get(t));
switch (t) {
case json_type::array:
return (*this).get_array().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
case json_type::object:
return (*this).get_object().for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
default:
return INVALID_JSON_POINTER;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::simdjson_result(
rvv_vls::ondemand::document &&value
) noexcept :
implementation_simdjson_result_base<rvv_vls::ondemand::document>(
std::forward<rvv_vls::ondemand::document>(value)
)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<rvv_vls::ondemand::document>(
error
)
{
}
simdjson_inline simdjson_result<size_t> simdjson_result<rvv_vls::ondemand::document>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<rvv_vls::ondemand::document>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::document>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::document>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array> simdjson_result<rvv_vls::ondemand::document>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::object> simdjson_result<rvv_vls::ondemand::document>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<rvv_vls::ondemand::document>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<rvv_vls::ondemand::document>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<rvv_vls::ondemand::document>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<rvv_vls::ondemand::document>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<rvv_vls::ondemand::document>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<rvv_vls::ondemand::document>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<rvv_vls::ondemand::document>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<rvv_vls::ondemand::document>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<rvv_vls::ondemand::document>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<rvv_vls::ondemand::document>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<rvv_vls::ondemand::document>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<rvv_vls::ondemand::document>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<rvv_vls::ondemand::document>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<rvv_vls::ondemand::document>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<rvv_vls::ondemand::document>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<rvv_vls::ondemand::document>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::document>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<rvv_vls::ondemand::document>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::document>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::raw_json_string> simdjson_result<rvv_vls::ondemand::document>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<rvv_vls::ondemand::document>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_deprecated simdjson_inline simdjson_result<T> simdjson_result<rvv_vls::ondemand::document>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<rvv_vls::ondemand::document>(first).get<T>();
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template<typename T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<rvv_vls::ondemand::document>(first).get<T>(out);
}
template<> simdjson_inline simdjson_result<rvv_vls::ondemand::document> simdjson_result<rvv_vls::ondemand::document>::get<rvv_vls::ondemand::document>() & noexcept = delete;
template<> simdjson_deprecated simdjson_inline simdjson_result<rvv_vls::ondemand::document> simdjson_result<rvv_vls::ondemand::document>::get<rvv_vls::ondemand::document>() && noexcept {
if (error()) { return error(); }
return std::forward<rvv_vls::ondemand::document>(first);
}
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document>::get<rvv_vls::ondemand::document>(rvv_vls::ondemand::document &out) & noexcept = delete;
template<> simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document>::get<rvv_vls::ondemand::document>(rvv_vls::ondemand::document &out) && noexcept {
if (error()) { return error(); }
out = std::forward<rvv_vls::ondemand::document>(first);
return SUCCESS;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::json_type> simdjson_result<rvv_vls::ondemand::document>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
simdjson_inline bool simdjson_result<rvv_vls::ondemand::document>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<rvv_vls::number_type> simdjson_result<rvv_vls::ondemand::document>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::number> simdjson_result<rvv_vls::ondemand::document>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T, typename std::enable_if<std::is_same<T, rvv_vls::ondemand::document>::value == false>::type>
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator T() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator rvv_vls::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator rvv_vls::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator rvv_vls::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document>::operator rvv_vls::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<rvv_vls::ondemand::document>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline bool simdjson_result<rvv_vls::ondemand::document>::at_end() const noexcept {
if (error()) { return error(); }
return first.at_end();
}
simdjson_inline int32_t simdjson_result<rvv_vls::ondemand::document>::current_depth() const noexcept {
if (error()) { return error(); }
return first.current_depth();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::document>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document>::at_path(std::string_view json_path) noexcept {
if (error()) { return error(); }
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {}
simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {}
simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); }
simdjson_inline simdjson_result<array> document_reference::get_array() & noexcept { return doc->get_array(); }
simdjson_inline simdjson_result<object> document_reference::get_object() & noexcept { return doc->get_object(); }
/**
* The document_reference instances are used primarily/solely for streams of JSON
* documents.
* We decided that calling 'get_double()' on the JSON document '1.233 blabla' should
* give an error, so we check for trailing content.
*
* However, for streams of JSON documents, we want to be able to start from
* "321" "321" "321"
* and parse it successfully as a stream of JSON documents, calling get_uint64_in_string()
* successfully each time.
*
* To achieve this result, we pass a 'false' to a get_root_value_iterator() method:
* this indicates that we allow trailing content.
*/
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); }
simdjson_inline simdjson_result<uint64_t> document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); }
simdjson_inline simdjson_result<int64_t> document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); }
simdjson_inline simdjson_result<uint32_t> document_reference::get_uint32() noexcept { return narrow_integer<uint32_t>(get_uint64()); }
simdjson_inline simdjson_result<int32_t> document_reference::get_int32() noexcept { return narrow_integer<int32_t>(get_int64()); }
simdjson_inline simdjson_result<uint16_t> document_reference::get_uint16() noexcept { return narrow_integer<uint16_t>(get_uint64()); }
simdjson_inline simdjson_result<int16_t> document_reference::get_int16() noexcept { return narrow_integer<int16_t>(get_int64()); }
simdjson_inline simdjson_result<uint8_t> document_reference::get_uint8() noexcept { return narrow_integer<uint8_t>(get_uint64()); }
simdjson_inline simdjson_result<int8_t> document_reference::get_int8() noexcept { return narrow_integer<int8_t>(get_int64()); }
simdjson_inline simdjson_result<double> document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); }
simdjson_inline simdjson_result<double> document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double_in_string(false); }
simdjson_inline simdjson_result<float> document_reference::get_float() noexcept { return doc->get_root_value_iterator().get_root_float(false); }
simdjson_inline simdjson_result<float> document_reference::get_float_in_string() noexcept { return doc->get_root_value_iterator().get_root_float_in_string(false); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> document_reference::get_float32() noexcept {
float result;
SIMDJSON_TRY(get_float().get(result));
return static_cast<std::float32_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> document_reference::get_float64() noexcept {
double result;
SIMDJSON_TRY(get_double().get(result));
return static_cast<std::float64_t>(result);
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); }
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> document_reference::get_u8string(bool allow_replacement) noexcept {
std::string_view content;
SIMDJSON_TRY( get_string(allow_replacement).get(content) );
return internal::as_u8string_view(content);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); }
simdjson_inline simdjson_result<std::string_view> document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); }
simdjson_inline simdjson_result<raw_json_string> document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); }
simdjson_inline simdjson_result<bool> document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); }
simdjson_inline simdjson_result<value> document_reference::get_value() noexcept { return doc->get_value(); }
simdjson_inline simdjson_result<bool> document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); }
template<> simdjson_inline simdjson_result<array> document_reference::get() & noexcept { return get_array(); }
template<> simdjson_inline simdjson_result<object> document_reference::get() & noexcept { return get_object(); }
template<> simdjson_inline simdjson_result<raw_json_string> document_reference::get() & noexcept { return get_raw_json_string(); }
template<> simdjson_inline simdjson_result<std::string_view> document_reference::get() & noexcept { return get_string(false); }
#if SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<std::u8string_view> document_reference::get() & noexcept { return get_u8string(false); }
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<> simdjson_inline simdjson_result<double> document_reference::get() & noexcept { return get_double(); }
template<> simdjson_inline simdjson_result<float> document_reference::get() & noexcept { return get_float(); }
template<> simdjson_inline simdjson_result<uint64_t> document_reference::get() & noexcept { return get_uint64(); }
template<> simdjson_inline simdjson_result<int64_t> document_reference::get() & noexcept { return get_int64(); }
template<> simdjson_inline simdjson_result<uint32_t> document_reference::get() & noexcept { return get_uint32(); }
template<> simdjson_inline simdjson_result<int32_t> document_reference::get() & noexcept { return get_int32(); }
template<> simdjson_inline simdjson_result<uint16_t> document_reference::get() & noexcept { return get_uint16(); }
template<> simdjson_inline simdjson_result<int16_t> document_reference::get() & noexcept { return get_int16(); }
template<> simdjson_inline simdjson_result<uint8_t> document_reference::get() & noexcept { return get_uint8(); }
template<> simdjson_inline simdjson_result<int8_t> document_reference::get() & noexcept { return get_int8(); }
#if SIMDJSON_SUPPORTS_FLOAT32_T
template<> simdjson_inline simdjson_result<std::float32_t> document_reference::get() & noexcept { return get_float32(); }
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<std::float64_t> document_reference::get() & noexcept { return get_float64(); }
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
template<> simdjson_inline simdjson_result<bool> document_reference::get() & noexcept { return get_bool(); }
template<> simdjson_inline simdjson_result<value> document_reference::get() & noexcept { return get_value(); }
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline document_reference::operator T() noexcept(false) { return get<T>(); }
simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); }
simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); }
simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); }
simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); }
simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); }
simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); }
simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); }
simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); }
simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); }
#endif
simdjson_inline simdjson_result<size_t> document_reference::count_elements() & noexcept { return doc->count_elements(); }
simdjson_inline simdjson_result<size_t> document_reference::count_fields() & noexcept { return doc->count_fields(); }
simdjson_inline simdjson_result<value> document_reference::at(size_t index) & noexcept { return doc->at(index); }
simdjson_inline simdjson_result<array_iterator> document_reference::begin() & noexcept { return doc->begin(); }
simdjson_inline simdjson_result<array_iterator> document_reference::end() & noexcept { return doc->end(); }
simdjson_inline simdjson_result<value> document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); }
simdjson_inline simdjson_result<value> document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<value> document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); }
simdjson_inline simdjson_result<json_type> document_reference::type() noexcept { return doc->type(); }
simdjson_inline simdjson_result<bool> document_reference::is_scalar() noexcept { return doc->is_scalar(); }
simdjson_inline simdjson_result<bool> document_reference::is_string() noexcept { return doc->is_string(); }
simdjson_inline simdjson_result<const char *> 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(); }
simdjson_inline simdjson_result<bool> document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); }
simdjson_inline simdjson_result<number_type> document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); }
simdjson_inline simdjson_result<number> document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json_token() noexcept { return doc->raw_json_token(); }
simdjson_inline simdjson_result<value> document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); }
simdjson_inline simdjson_result<value> document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); }
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
simdjson_inline error_code document_reference::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { return doc->for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); }
simdjson_inline simdjson_result<std::string_view> document_reference::raw_json() noexcept { return doc->raw_json();}
simdjson_inline document_reference::operator document&() const noexcept { return *doc; }
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code document_reference::extract_into(T& out) & noexcept {
return doc->extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::simdjson_result(rvv_vls::ondemand::document_reference value, error_code error)
noexcept : implementation_simdjson_result_base<rvv_vls::ondemand::document_reference>(std::forward<rvv_vls::ondemand::document_reference>(value), error) {}
simdjson_inline simdjson_result<size_t> simdjson_result<rvv_vls::ondemand::document_reference>::count_elements() & noexcept {
if (error()) { return error(); }
return first.count_elements();
}
simdjson_inline simdjson_result<size_t> simdjson_result<rvv_vls::ondemand::document_reference>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::at(size_t index) & noexcept {
if (error()) { return error(); }
return first.at(index);
}
simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document_reference>::rewind() noexcept {
if (error()) { return error(); }
first.rewind();
return SUCCESS;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::document_reference>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array_iterator> simdjson_result<rvv_vls::ondemand::document_reference>::end() & noexcept {
return {};
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::find_field_unordered(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::operator[](const char *key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::find_field(const char *key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::array> simdjson_result<rvv_vls::ondemand::document_reference>::get_array() & noexcept {
if (error()) { return error(); }
return first.get_array();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::object> simdjson_result<rvv_vls::ondemand::document_reference>::get_object() & noexcept {
if (error()) { return error(); }
return first.get_object();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_uint64() noexcept {
if (error()) { return error(); }
return first.get_uint64();
}
simdjson_inline simdjson_result<uint64_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_uint64_in_string() noexcept {
if (error()) { return error(); }
return first.get_uint64_in_string();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_int64() noexcept {
if (error()) { return error(); }
return first.get_int64();
}
simdjson_inline simdjson_result<int64_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_int64_in_string() noexcept {
if (error()) { return error(); }
return first.get_int64_in_string();
}
simdjson_inline simdjson_result<uint32_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_uint32() noexcept {
if (error()) { return error(); }
return first.get_uint32();
}
simdjson_inline simdjson_result<int32_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_int32() noexcept {
if (error()) { return error(); }
return first.get_int32();
}
simdjson_inline simdjson_result<uint16_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_uint16() noexcept {
if (error()) { return error(); }
return first.get_uint16();
}
simdjson_inline simdjson_result<int16_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_int16() noexcept {
if (error()) { return error(); }
return first.get_int16();
}
simdjson_inline simdjson_result<uint8_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_uint8() noexcept {
if (error()) { return error(); }
return first.get_uint8();
}
simdjson_inline simdjson_result<int8_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_int8() noexcept {
if (error()) { return error(); }
return first.get_int8();
}
simdjson_inline simdjson_result<double> simdjson_result<rvv_vls::ondemand::document_reference>::get_double() noexcept {
if (error()) { return error(); }
return first.get_double();
}
simdjson_inline simdjson_result<double> simdjson_result<rvv_vls::ondemand::document_reference>::get_double_in_string() noexcept {
if (error()) { return error(); }
return first.get_double_in_string();
}
simdjson_inline simdjson_result<float> simdjson_result<rvv_vls::ondemand::document_reference>::get_float() noexcept {
if (error()) { return error(); }
return first.get_float();
}
simdjson_inline simdjson_result<float> simdjson_result<rvv_vls::ondemand::document_reference>::get_float_in_string() noexcept {
if (error()) { return error(); }
return first.get_float_in_string();
}
#if SIMDJSON_SUPPORTS_FLOAT32_T
simdjson_inline simdjson_result<std::float32_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_float32() noexcept {
if (error()) { return error(); }
return first.get_float32();
}
#endif // SIMDJSON_SUPPORTS_FLOAT32_T
#if SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::float64_t> simdjson_result<rvv_vls::ondemand::document_reference>::get_float64() noexcept {
if (error()) { return error(); }
return first.get_float64();
}
#endif // SIMDJSON_SUPPORTS_FLOAT64_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::document_reference>::get_string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<rvv_vls::ondemand::document_reference>::get_u8string(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_u8string(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document_reference>::get_string(string_type& receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.get_string(receiver, allow_replacement);
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::document_reference>::get_wobbly_string() noexcept {
if (error()) { return error(); }
return first.get_wobbly_string();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::raw_json_string> simdjson_result<rvv_vls::ondemand::document_reference>::get_raw_json_string() noexcept {
if (error()) { return error(); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document_reference>::get_bool() noexcept {
if (error()) { return error(); }
return first.get_bool();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::get_value() noexcept {
if (error()) { return error(); }
return first.get_value();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document_reference>::is_null() noexcept {
if (error()) { return error(); }
return first.is_null();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<rvv_vls::ondemand::document_reference>::get() &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>();
}
template<typename T>
simdjson_inline simdjson_result<T> simdjson_result<rvv_vls::ondemand::document_reference>::get() &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<rvv_vls::ondemand::document_reference>(first).get<T>();
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document_reference>::get(T &out) &
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return first.get<T>(out);
}
template <class T>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document_reference>::get(T &out) &&
#if SIMDJSON_SUPPORTS_CONCEPTS
noexcept(nothrow_gettable<T, rvv_vls::ondemand::document_reference>)
#else
noexcept
#endif
{
if (error()) { return error(); }
return std::forward<rvv_vls::ondemand::document_reference>(first).get<T>(out);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::json_type> simdjson_result<rvv_vls::ondemand::document_reference>::type() noexcept {
if (error()) { return error(); }
return first.type();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document_reference>::is_scalar() noexcept {
if (error()) { return error(); }
return first.is_scalar();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document_reference>::is_string() noexcept {
if (error()) { return error(); }
return first.is_string();
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document_reference>::get(rvv_vls::ondemand::document_reference &out) & noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
template <>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document_reference>::get(rvv_vls::ondemand::document_reference &out) && noexcept {
if (error()) { return error(); }
out = first;
return SUCCESS;
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document_reference>::is_negative() noexcept {
if (error()) { return error(); }
return first.is_negative();
}
simdjson_inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::document_reference>::is_integer() noexcept {
if (error()) { return error(); }
return first.is_integer();
}
simdjson_inline simdjson_result<rvv_vls::number_type> simdjson_result<rvv_vls::ondemand::document_reference>::get_number_type() noexcept {
if (error()) { return error(); }
return first.get_number_type();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::number> simdjson_result<rvv_vls::ondemand::document_reference>::get_number() noexcept {
if (error()) { return error(); }
return first.get_number();
}
#if SIMDJSON_EXCEPTIONS
template <class T>
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator T() noexcept(false) {
static_assert(std::is_same<T, rvv_vls::ondemand::document_reference>::value == false, "You should not call get<T> when T is a document");
static_assert(std::is_same<T, rvv_vls::ondemand::document>::value == false, "You should not call get<T> when T is a document");
if (error()) { throw simdjson_error(error()); }
return first.get<T>();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator rvv_vls::ondemand::array() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator rvv_vls::ondemand::object() & noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator uint64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_uint64();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator int64_t() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_int64();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator double() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_double();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator std::string_view() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_string();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator rvv_vls::ondemand::raw_json_string() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_raw_json_string();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator bool() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first.get_bool();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_reference>::operator rvv_vls::ondemand::value() noexcept(false) {
if (error()) { throw simdjson_error(error()); }
return first;
}
#endif
simdjson_inline simdjson_result<const char *> simdjson_result<rvv_vls::ondemand::document_reference>::current_location() noexcept {
if (error()) { return error(); }
return first.current_location();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::document_reference>::raw_json_token() noexcept {
if (error()) { return error(); }
return first.raw_json_token();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::document_reference>::at_path(std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document_reference>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) {
return error();
}
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::document_reference>::extract_into(T& out) & noexcept {
if (error()) { return error(); }
return first.extract_into<FieldNames...>(out);
}
#endif // SIMDJSON_STATIC_REFLECTION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H
/* end file simdjson/generic/ondemand/document-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/document_stream-inl.h for rvv_vls: #include "simdjson/generic/ondemand/document_stream-inl.h" */
/* begin file simdjson/generic/ondemand/document_stream-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <algorithm>
#include <cstring>
#include <stdexcept>
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
#ifdef SIMDJSON_THREADS_ENABLED
inline void stage1_worker::finish() {
// After calling "run" someone would call finish() to wait
// for the end of the processing.
// This function will wait until either the thread has done
// the processing or, else, the destructor has been called.
std::unique_lock<std::mutex> lock(locking_mutex);
cond_var.wait(lock, [this]{return has_work == false;});
}
inline stage1_worker::~stage1_worker() {
// The thread may never outlive the stage1_worker instance
// and will always be stopped/joined before the stage1_worker
// instance is gone.
stop_thread();
}
inline void stage1_worker::start_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
if(thread.joinable()) {
return; // This should never happen but we never want to create more than one thread.
}
thread = std::thread([this]{
while(true) {
std::unique_lock<std::mutex> thread_lock(locking_mutex);
// We wait for either "run" or "stop_thread" to be called.
cond_var.wait(thread_lock, [this]{return has_work || !can_work;});
// If, for some reason, the stop_thread() method was called (i.e., the
// destructor of stage1_worker is called, then we want to immediately destroy
// the thread (and not do any more processing).
if(!can_work) {
break;
}
this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser,
this->_next_batch_start);
this->has_work = false;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify "finish"
thread_lock.unlock();
}
}
);
}
inline void stage1_worker::stop_thread() {
std::unique_lock<std::mutex> lock(locking_mutex);
// We have to make sure that all locks can be released.
can_work = false;
has_work = false;
cond_var.notify_all();
lock.unlock();
if(thread.joinable()) {
thread.join();
}
}
inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) {
std::unique_lock<std::mutex> lock(locking_mutex);
owner = ds;
_next_batch_start = next_batch_start;
stage1_thread_parser = stage1;
has_work = true;
// The condition variable call should be moved after thread_lock.unlock() for performance
// reasons but thread sanitizers may report it as a data race if we do.
// See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock
cond_var.notify_one(); // will notify the thread lock that we have work
lock.unlock();
}
#endif // SIMDJSON_THREADS_ENABLED
simdjson_inline document_stream::document_stream(
ondemand::parser &_parser,
const uint8_t *_buf,
size_t _len,
size_t _batch_size,
bool _allow_comma_separated,
stream_format _format
) noexcept
: parser{&_parser},
buf{_buf},
len{_len},
batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size},
allow_comma_separated{_allow_comma_separated},
format{_format},
error{SUCCESS}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change
#endif
{
}
simdjson_inline document_stream::document_stream() noexcept
: parser{nullptr},
buf{nullptr},
len{0},
batch_size{0},
allow_comma_separated{false},
format{stream_format::whitespace_delimited},
error{UNINITIALIZED}
#ifdef SIMDJSON_THREADS_ENABLED
, use_thread(false)
#endif
{
}
simdjson_inline document_stream::~document_stream() noexcept
{
#ifdef SIMDJSON_THREADS_ENABLED
worker.reset();
#endif
}
inline size_t document_stream::size_in_bytes() const noexcept {
return len;
}
inline size_t document_stream::truncated_bytes() const noexcept {
// Stage 1 returns EMPTY on zero-length input before it writes the index
// sentinels read below, so they would still hold a previous stream's values.
if (len == 0) { return 0; }
if(error == CAPACITY) { return len - batch_start; }
return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1];
}
simdjson_inline document_stream::iterator::iterator() noexcept
: stream{nullptr}, finished{true} {
}
simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept
: stream{_stream}, finished{is_end} {
}
simdjson_inline simdjson_result<ondemand::document_reference> document_stream::iterator::operator*() noexcept {
return simdjson_result<ondemand::document_reference>(stream->doc, stream->error);
}
simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept {
// If there is an error, then we want the iterator
// to be finished, no matter what. (E.g., we do not
// keep generating documents with errors, or go beyond
// a document with errors.)
//
// Users do not have to call "operator*()" when they use operator++,
// so we need to end the stream in the operator++ function.
//
// Note that setting finished = true is essential otherwise
// we would enter an infinite loop.
if (stream->error) { finished = true; }
// Note that stream->error() is guarded against error conditions
// (it will immediately return if stream->error casts to false).
// In effect, this next function does nothing when (stream->error)
// is true (hence the risk of an infinite loop).
stream->next();
// If that was the last document, we're finished.
// It is the only type of error we do not want to appear
// in operator*.
if (stream->error == EMPTY) { finished = true; }
// If we had any other kind of error (not EMPTY) then we want
// to pass it along to the operator* and we cannot mark the result
// as "finished" just yet.
return *this;
}
simdjson_inline bool document_stream::iterator::at_end() const noexcept {
return finished;
}
simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept {
return finished != other.finished;
}
simdjson_inline bool document_stream::iterator::operator==(const document_stream::iterator &other) const noexcept {
return finished == other.finished;
}
simdjson_inline document_stream::iterator document_stream::begin() noexcept {
start();
// If there are no documents, we're finished.
return iterator(this, error == EMPTY);
}
simdjson_inline document_stream::iterator document_stream::end() noexcept {
return iterator(this, true);
}
inline void document_stream::start() noexcept {
if (error) { return; }
error = parser->allocate(batch_size);
if (error) { return; }
// Always run the first stage 1 parse immediately
batch_start = 0;
error = run_stage1(*parser, batch_start);
while(error == EMPTY) {
// In exceptional cases, we may start with an empty block
batch_start = next_batch_start();
if (batch_start >= len) { return; }
error = run_stage1(*parser, batch_start);
}
if (error) { return; }
// For json_sequence mode, structural_indexes[0] points to the actual JSON value
// after the RS delimiter and any following whitespace. For regular mode, it is
// the offset from batch_start to the first document in the batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
doc = document(json_iterator(&buf[batch_start], parser));
doc.iter._streaming = true;
#ifdef SIMDJSON_THREADS_ENABLED
if (use_thread && next_batch_start() < len) {
// Kick off the first thread on next batch if needed
if (worker.get() == nullptr) {
worker.reset(new(std::nothrow) stage1_worker());
if (worker.get() == nullptr) { error = MEMALLOC; return; }
}
error = stage1_thread_parser.allocate(batch_size);
if (error) { return; }
worker->start_thread();
start_stage1_thread();
if (error) { return; }
}
#endif // SIMDJSON_THREADS_ENABLED
}
inline void document_stream::next() noexcept {
// We always enter at once once in an error condition.
if (error) { return; }
next_document();
if (error) { return; }
auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get();
doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index];
// Check if at end of structural indexes (i.e. at end of batch)
if(cur_struct_index >= static_cast<int64_t>(parser->implementation->n_structural_indexes)) {
error = EMPTY;
// Load another batch (if available)
while (error == EMPTY) {
batch_start = next_batch_start();
if (batch_start >= len) { break; }
#ifdef SIMDJSON_THREADS_ENABLED
if(use_thread) {
load_from_stage1_thread();
} else {
error = run_stage1(*parser, batch_start);
}
#else
error = run_stage1(*parser, batch_start);
#endif
/**
* Whenever we move to another window, we need to update all pointers to make
* it appear as if the input buffer started at the beginning of the window.
*
* Take this input:
*
* {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311]
*
* Say you process the following window...
*
* '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]'
*
* When you do so, the json_iterator has a pointer at the beginning of the memory region
* (pointing at the beginning of '{"z"...'.
*
* When you move to the window that starts at...
*
* '[7, 10, 9] [15, 11, 12, 13] ...
*
* then it is not sufficient to just run stage 1. You also need to re-anchor the
* json_iterator so that it believes we are starting at '[7, 10, 9]...'.
*
* Under the DOM front-end, this gets done automatically because the parser owns
* the pointer the data, and when you call stage1 and then stage2 on the same
* parser, then stage2 will run on the pointer acquired by stage1.
*
* That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that
* we used. But json_iterator has no callback when stage1 is called on the parser.
* In fact, I think that the parser is unaware of json_iterator.
*
*
* So we need to re-anchor the json_iterator after each call to stage 1 so that
* all of the pointers are in sync.
*/
doc.iter = json_iterator(&buf[batch_start], parser);
doc.iter._streaming = true;
/**
* End of resync.
*/
if (error) { continue; } // If the error was EMPTY, we may want to load another batch.
doc_index = batch_start + parser->implementation->structural_indexes[0];
}
}
}
simdjson_inline uint8_t document_stream::document_delimiter() const noexcept {
switch (format) {
case stream_format::newline_delimited: return '\n';
case stream_format::json_sequence: return 0x1E;
default: return 0;
}
}
simdjson_inline bool document_stream::skip_to_delimiter(uint8_t delimiter) noexcept {
const uint8_t *const base = &buf[batch_start];
const token_position pos = doc.iter.position();
const token_position end = doc.iter.end_position();
if (pos >= end) { return false; }
const size_t here = size_t(doc.iter.token.peek(pos) - base);
const size_t batch_len =
(len - batch_start < batch_size) ? len - batch_start : batch_size;
if (here >= batch_len) { return false; }
const uint8_t *const found = static_cast<const uint8_t *>(
std::memchr(base + here, delimiter, batch_len - here));
if (found == nullptr) { return false; }
const uint32_t boundary = uint32_t(found - base);
// The answer is near `pos`: the delimiter ends the current document, while
// `end` spans the whole batch. Gallop first so the cost follows the distance
// rather than the size of the batch.
token_position lo = pos;
size_t hop = 1;
while (lo + hop < end && lo[hop] < boundary) { lo += hop; hop <<= 1; }
token_position hi = (lo + hop < end) ? lo + hop : end;
while (lo < hi) {
const token_position mid = lo + ((hi - lo) >> 1);
if (*mid < boundary) { lo = mid + 1; } else { hi = mid; }
}
doc.iter.token.set_position(lo);
return true;
}
inline void document_stream::next_document() noexcept {
// A delimiter that cannot occur inside a document tells us where the current
// one ends, so we can jump there instead of walking every structural. Only
// valid while the iterator is still inside the document: a consumed document
// already sits on the next one's first token, and skip_child() returns at
// once for it.
//
// The jump does not structure-validate the unread remainder of the current
// document: under newline_delimited / json_sequence the next delimiter is
// assumed to be the true document boundary. Callers that leave depth() > 0
// while violating that contract (e.g. pretty multi-line JSON under
// newline_delimited) can mis-align following documents; use
// whitespace_delimited if unsure.
const uint8_t delimiter = document_delimiter();
if (delimiter != 0 && !error && doc.iter.depth() > 0 &&
skip_to_delimiter(delimiter)) {
doc.iter._depth = 1;
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
return;
}
// Go to next place where depth=0 (document depth)
error = doc.iter.skip_child(0);
if (error) { return; }
// Always set depth=1 at the start of document
doc.iter._depth = 1;
// consume comma if comma separated is allowed
if (allow_comma_separated) {
error_code ignored = doc.iter.consume_character(',');
static_cast<void>(ignored); // ignored on purpose
}
// Resets the string buffer at the beginning, thus invalidating the strings.
doc.iter._string_buf_loc = parser->string_buf.get();
doc.iter._root = doc.iter.position();
}
inline size_t document_stream::next_batch_start() const noexcept {
return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes];
}
inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept {
// This code only updates the structural index in the parser, it does not update any json_iterator
// instance.
size_t remaining = len - _batch_start;
stage1_mode mode;
if (remaining <= batch_size) {
// Final batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_final;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_final;
break;
default:
mode = stage1_mode::streaming_final;
break;
}
return p.implementation->stage1(&buf[_batch_start], remaining, mode);
} else {
// Partial batch
switch (format) {
case stream_format::json_sequence:
mode = stage1_mode::json_sequence_partial;
break;
case stream_format::comma_delimited:
mode = stage1_mode::comma_delimited_partial;
break;
default:
mode = stage1_mode::streaming_partial;
break;
}
return p.implementation->stage1(&buf[_batch_start], batch_size, mode);
}
}
simdjson_inline size_t document_stream::iterator::current_index() const noexcept {
return stream->doc_index;
}
simdjson_inline std::string_view document_stream::iterator::source() const noexcept {
// On error (e.g., CAPACITY), there is no document to walk: return the rest of
// the input, as the DOM document_stream does.
if (stream->error) {
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->len - current_index());
}
// Always walk from the root of the document, whatever the current position
// of the document iterator: the user may have already consumed part of the
// document, so the iterator's current depth must not be used here.
depth_t depth = 1;
auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get();
// Process the first token to determine if scalar value
{
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[': // Depth=1 already at start of document
break;
case '}': case ']':
depth--;
break;
default: // Scalar value document
// This returns a string spanning from start of value to the beginning of the next document (excluded)
{
auto next_index = stream->batch_start + stream->parser->implementation->structural_indexes[++cur_struct_index];
// normally the length would be next_index - current_index() - 1, except for the last document
size_t svlen = next_index - current_index();
const char *start = reinterpret_cast<const char*>(stream->buf) + current_index();
// When the scalar is followed by a truncated document, the structural
// indexes of that document were dropped and next_index is the end of
// the input, so we bound the scalar by scanning the token itself.
size_t token_len = 0;
if (*start == '"') {
token_len = 1;
while (token_len < svlen) {
char c = start[token_len++];
if (c == '\\') {
token_len++;
} else if (c == '"') {
break;
}
}
} else {
while (token_len < svlen) {
char c = start[token_len];
if (std::isspace(static_cast<unsigned char>(c)) || c == ',' || c == '{' || c == '[' || c == '\0' || static_cast<uint8_t>(c) == 0x1E) {
break;
}
token_len++;
}
}
if (token_len > 0 && token_len < svlen) {
svlen = token_len;
}
// Trim trailing whitespace, NUL, and RS (0x1E). In RFC 7464
// json_sequence mode the scanner classifies RS as a scalar
// character, so an RS-prefixed scalar document (number / true /
// false / null / string) has no closing structural index and the
// slice runs all the way up to the next document's RS. RS cannot
// legally appear in a JSON value at the source level (control
// characters in strings must be escaped as \u001E), so stripping
// it is safe in every stream_format.
while(svlen > 1 && (std::isspace(static_cast<unsigned char>(start[svlen-1])) || start[svlen-1] == '\0' || static_cast<uint8_t>(start[svlen-1]) == 0x1E || (stream->format == stream_format::comma_delimited && start[svlen-1] == ','))) {
svlen--;
}
return std::string_view(start, svlen);
}
}
cur_struct_index++;
}
while (cur_struct_index <= static_cast<int64_t>(stream->parser->implementation->n_structural_indexes)) {
switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) {
case '{': case '[':
depth++;
break;
case '}': case ']':
depth--;
break;
}
if (depth == 0) { break; }
cur_struct_index++;
}
return std::string_view(reinterpret_cast<const char*>(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);;
}
inline error_code document_stream::iterator::error() const noexcept {
return stream->error;
}
#ifdef SIMDJSON_THREADS_ENABLED
inline void document_stream::load_from_stage1_thread() noexcept {
worker->finish();
// Swap to the parser that was loaded up in the thread. Make sure the parser has
// enough memory to swap to, as well.
std::swap(stage1_thread_parser,*parser);
error = stage1_thread_error;
if (error) { return; }
// If there's anything left, start the stage 1 thread!
if (next_batch_start() < len) {
start_stage1_thread();
}
}
inline void document_stream::start_stage1_thread() noexcept {
// we call the thread on a lambda that will update
// this->stage1_thread_error
// there is only one thread that may write to this value
// TODO this is NOT exception-safe.
this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error
size_t _next_batch_start = this->next_batch_start();
worker->run(this, & this->stage1_thread_parser, _next_batch_start);
}
#endif // SIMDJSON_THREADS_ENABLED
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::document_stream>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<rvv_vls::ondemand::document_stream>(error)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::document_stream>::simdjson_result(
rvv_vls::ondemand::document_stream &&value
) noexcept :
implementation_simdjson_result_base<rvv_vls::ondemand::document_stream>(
std::forward<rvv_vls::ondemand::document_stream>(value)
)
{
}
}
#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H
/* end file simdjson/generic/ondemand/document_stream-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/field-inl.h for rvv_vls: #include "simdjson/generic/ondemand/field-inl.h" */
/* begin file simdjson/generic/ondemand/field-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
// clang 6 does not think the default constructor can be noexcept, so we make it explicit
simdjson_inline field::field() noexcept : std::pair<raw_json_string, ondemand::value>() {}
simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept
: std::pair<raw_json_string, ondemand::value>(key, std::forward<ondemand::value>(value))
{
}
simdjson_inline simdjson_result<field> field::start(value_iterator &parent_iter) noexcept {
raw_json_string key;
SIMDJSON_TRY( parent_iter.field_key().get(key) );
SIMDJSON_TRY( parent_iter.field_value() );
return field::start(parent_iter, key);
}
simdjson_inline simdjson_result<field> field::start(const value_iterator &parent_iter, raw_json_string key) noexcept {
return field(key, parent_iter.child());
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> field::unescaped_key(bool allow_replacement) noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us.
simdjson_result<std::string_view> answer = first.unescape(second.iter.json_iter(), allow_replacement);
first.consume();
return answer;
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_warn_unused simdjson_result<std::u8string_view> field::unescaped_u8key(bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
return internal::as_u8string_view(key);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template <typename string_type>
simdjson_inline simdjson_warn_unused error_code field::unescaped_key(string_type& receiver, bool allow_replacement) noexcept {
std::string_view key;
SIMDJSON_TRY( unescaped_key(allow_replacement).get(key) );
internal::assign_utf8(receiver, key);
return SUCCESS;
}
simdjson_inline raw_json_string field::key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
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<const char*>(first.buf-1), second.iter._json_iter->token.peek(-1) - first.buf + 1);
}
simdjson_inline std::string_view field::escaped_key() const noexcept {
SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us.
auto end_quote = second.iter._json_iter->token.peek(-1);
while(*end_quote != '"') end_quote--;
return std::string_view(reinterpret_cast<const char*>(first.buf), end_quote - first.buf);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline std::u8string_view field::escaped_u8key() const noexcept {
return internal::as_u8string_view(escaped_key());
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline value &field::value() & noexcept {
return second;
}
simdjson_inline value field::value() && noexcept {
return std::forward<field>(*this).second;
}
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::field>::simdjson_result(
rvv_vls::ondemand::field &&value
) noexcept :
implementation_simdjson_result_base<rvv_vls::ondemand::field>(
std::forward<rvv_vls::ondemand::field>(value)
)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::field>::simdjson_result(
error_code error
) noexcept :
implementation_simdjson_result_base<rvv_vls::ondemand::field>(error)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::raw_json_string> simdjson_result<rvv_vls::ondemand::field>::key() noexcept {
if (error()) { return error(); }
return first.key();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::field>::key_raw_json_token() noexcept {
if (error()) { return error(); }
return first.key_raw_json_token();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::field>::escaped_key() noexcept {
if (error()) { return error(); }
return first.escaped_key();
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<rvv_vls::ondemand::field>::escaped_u8key() noexcept {
if (error()) { return error(); }
return first.escaped_u8key();
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::field>::unescaped_key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(allow_replacement);
}
#if SIMDJSON_SUPPORTS_CHAR8_T
simdjson_inline simdjson_result<std::u8string_view> simdjson_result<rvv_vls::ondemand::field>::unescaped_u8key(bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_u8key(allow_replacement);
}
#endif // SIMDJSON_SUPPORTS_CHAR8_T
template<typename string_type>
simdjson_warn_unused simdjson_inline error_code simdjson_result<rvv_vls::ondemand::field>::unescaped_key(string_type &receiver, bool allow_replacement) noexcept {
if (error()) { return error(); }
return first.unescaped_key(receiver, allow_replacement);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::field>::value() noexcept {
if (error()) { return error(); }
return std::move(first.value());
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_FIELD_INL_H
/* end file simdjson/generic/ondemand/field-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/json_iterator-inl.h for rvv_vls: #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/json_iterator-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
: token(std::forward<token_iterator>(other.token)),
parser{other.parser},
_string_buf_loc{other._string_buf_loc},
error{other.error},
_depth{other._depth},
_root{other._root},
_streaming{other._streaming}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
, _allow_incomplete_json{other._allow_incomplete_json}
#endif
{
other.parser = nullptr;
}
simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
token = other.token;
parser = other.parser;
_string_buf_loc = other._string_buf_loc;
error = other.error;
_depth = other._depth;
_root = other._root;
_streaming = other._streaming;
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
_allow_incomplete_json = other._allow_incomplete_json;
#endif
other.parser = nullptr;
return *this;
}
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{false}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
: token(buf, &_parser->implementation->structural_indexes[0]),
parser{_parser},
_string_buf_loc{parser->string_buf.get()},
_depth{1},
_root{parser->implementation->structural_indexes.get()},
_streaming{streaming},
_allow_incomplete_json{true}
{
logger::log_headers();
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
inline void json_iterator::rewind() noexcept {
token.set_position( root_position() );
logger::log_headers(); // We start again
_string_buf_loc = parser->string_buf.get();
_depth = 1;
}
inline bool json_iterator::balanced() const noexcept {
token_iterator ti(token);
int32_t count{0};
ti.set_position( root_position() );
while(ti.peek() <= peek_last()) {
switch (*ti.return_current_and_advance())
{
case '[': case '{':
count++;
break;
case ']': case '}':
count--;
break;
default:
break;
}
}
return count == 0;
}
// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
// skip_child() function is not marked inline).
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
if (depth() <= parent_depth) { return SUCCESS; }
switch (*return_current_and_advance()) {
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
// For the first open array/object in a value, we've already incremented depth, so keep it the same
// We never stop at colon, but if we did, it wouldn't affect depth
case '[': case '{': case ':':
logger::log_start_value(*this, "skip");
break;
// If there is a comma, we have just finished a value in an array/object, and need to get back in
case ',':
logger::log_value(*this, "skip");
break;
// ] or } means we just finished a value and need to jump out of the array/object
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
#if SIMDJSON_CHECK_EOF
// If there are no more tokens, the parent is incomplete.
if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
#endif // SIMDJSON_CHECK_EOF
break;
case '"':
// At the end, peek() would read the sentinel, which points into the padding.
if(!at_end() && *peek() == ':') {
// We are at a key!!!
// This might happen if you just started an object and you skip it immediately.
// Performance note: it would be nice to get rid of this check as it is somewhat
// expensive.
// https://github.com/simdjson/simdjson/issues/1742
logger::log_value(*this, "key");
return_current_and_advance(); // eat up the ':'
break; // important!!!
}
simdjson_fallthrough;
// Anything else must be a scalar value
default:
// For the first scalar, we will have incremented depth already, so we decrement it here.
logger::log_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
}
// Now that we've considered the first value, we only increment/decrement for arrays/objects
while (position() < end_position()) {
switch (*return_current_and_advance()) {
case '[': case '{':
logger::log_start_value(*this, "skip");
_depth++;
break;
// TODO consider whether matching braces is a requirement: if non-matching braces indicates
// *missing* braces, then future lookups are not in the object/arrays they think they are,
// violating the rule "validate enough structure that the user can be confident they are
// looking at the right values."
// PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
case ']': case '}':
logger::log_end_value(*this, "skip");
_depth--;
if (depth() <= parent_depth) { return SUCCESS; }
break;
default:
logger::log_value(*this, "skip", "");
break;
}
}
return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
}
SIMDJSON_POP_DISABLE_WARNINGS
simdjson_inline bool json_iterator::at_root() const noexcept {
return position() == root_position();
}
simdjson_inline bool json_iterator::is_single_token() const noexcept {
return parser->implementation->n_structural_indexes == 1;
}
simdjson_inline bool json_iterator::streaming() const noexcept {
return _streaming;
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
return _allow_incomplete_json;
}
simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
const uint8_t *end = token.buf + parser->_document_len;
return json < end ? size_t(end - json) : 0;
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_inline token_position json_iterator::root_position() const noexcept {
return _root;
}
simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
}
simdjson_inline void json_iterator::assert_at_root() const noexcept {
SIMDJSON_ASSUME( _depth == 1 );
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME( token.position() == _root );
#endif
}
simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
assert_valid_position(token._position + required_tokens - 1);
}
simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
(void)position; // Suppress unused parameter warning
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
#endif
}
simdjson_inline bool json_iterator::at_end() const noexcept {
return position() == end_position();
}
simdjson_inline token_position json_iterator::end_position() const noexcept {
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
return &parser->implementation->structural_indexes[n_structural_indexes];
}
inline std::string json_iterator::to_string() const noexcept {
if( !is_alive() ) { return "dead json_iterator instance"; }
const char * current_structural = reinterpret_cast<const char *>(token.peek());
return std::string("json_iterator [ depth : ") + std::to_string(_depth)
+ std::string(", structural : '") + std::string(current_structural,1)
+ std::string("', offset : ") + std::to_string(token.current_offset())
+ std::string("', error : ") + error_message(error)
+ std::string(" ]");
}
inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
if (!is_alive()) { // Unrecoverable error
if (!at_root()) {
return reinterpret_cast<const char *>(token.peek(-1));
} else {
return reinterpret_cast<const char *>(token.peek());
}
}
if (at_end()) {
return OUT_OF_BOUNDS;
}
return reinterpret_cast<const char *>(token.peek());
}
simdjson_inline bool json_iterator::is_alive() const noexcept {
return parser;
}
simdjson_inline void json_iterator::abandon() noexcept {
parser = nullptr;
_depth = 0;
}
simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens();
#endif // SIMDJSON_CHECK_EOF
return token.return_current_and_advance();
}
simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
// deliberately done without safety guard:
return token.peek();
}
simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // SIMDJSON_CHECK_EOF
return token.peek(delta);
}
simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_more_tokens(delta+1);
#endif // #if SIMDJSON_CHECK_EOF
return token.peek_length(delta);
}
simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
// todo: currently we require end-of-string buffering, but the following
// assert_valid_position should be turned on if/when we lift that condition.
// assert_valid_position(position);
// This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
// is ON by default, we have no choice but to disable it for real with a comment.
return token.peek(position);
}
simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_length(position);
}
simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
#if SIMDJSON_CHECK_EOF
assert_valid_position(position);
#endif // SIMDJSON_CHECK_EOF
return token.peek_root_length(position);
}
simdjson_inline token_position json_iterator::last_position() const noexcept {
// The following line fails under some compilers...
// SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
// since it has side-effects.
uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
SIMDJSON_ASSUME(n_structural_indexes > 0);
return &parser->implementation->structural_indexes[n_structural_indexes - 1];
}
simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
return token.peek(last_position());
}
simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
SIMDJSON_ASSUME(_depth == parent_depth + 1);
_depth = parent_depth;
}
simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
_depth = child_depth;
}
simdjson_inline depth_t json_iterator::depth() const noexcept {
return _depth;
}
simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
return _string_buf_loc;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
logger::log_error(*this, message);
error = _error;
return error;
}
simdjson_inline token_position json_iterator::position() const noexcept {
return token.position();
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape(in, _string_buf_loc, allow_replacement);
#endif
}
simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
auto result = parser->unescape_wobbly(in, _string_buf_loc);
#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
// Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
// has side effects that will be discarded.
SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
return result;
#else
return parser->unescape_wobbly(in, _string_buf_loc);
#endif
}
simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
SIMDJSON_ASSUME(_depth == child_depth - 1);
#if SIMDJSON_DEVELOPMENT_CHECKS
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
#endif
#endif
token.set_position(position);
_depth = child_depth;
}
simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
if (*peek() == c) {
return_current_and_advance();
return SUCCESS;
}
return TAPE_ERROR;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
}
simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
}
#endif
simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
logger::log_error(*this, message);
return _error;
}
simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
// This function is not expected to be called in performance-sensitive settings.
// Let us guard against silly cases:
if((N < max_len) || (N == 0)) { return false; }
// Copy to the buffer.
std::memcpy(tmpbuf, json, max_len);
if(N > max_len) { // We pad whatever remains with ' '.
std::memset(tmpbuf + max_len, ' ', N - max_len);
}
return true;
}
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::json_iterator>::simdjson_result(rvv_vls::ondemand::json_iterator &&value) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::json_iterator>(std::forward<rvv_vls::ondemand::json_iterator>(value)) {}
simdjson_inline simdjson_result<rvv_vls::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::json_iterator>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/json_iterator-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/json_type-inl.h for rvv_vls: #include "simdjson/generic/ondemand/json_type-inl.h" */
/* begin file simdjson/generic/ondemand/json_type-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept {
switch (type) {
case json_type::array: out << "array"; break;
case json_type::object: out << "object"; break;
case json_type::number: out << "number"; break;
case json_type::string: out << "string"; break;
case json_type::boolean: out << "boolean"; break;
case json_type::null: out << "null"; break;
case json_type::unknown: out << "unknown"; break;
}
return out;
}
#if SIMDJSON_EXCEPTIONS
inline std::ostream& operator<<(std::ostream& out, simdjson_result<json_type> &type) noexcept(false) {
return out << type.value();
}
#endif
simdjson_inline number_type number::get_number_type() const noexcept {
return type;
}
simdjson_inline bool number::is_uint64() const noexcept {
return get_number_type() == number_type::unsigned_integer;
}
simdjson_inline uint64_t number::get_uint64() const noexcept {
return payload.unsigned_integer;
}
simdjson_inline number::operator uint64_t() const noexcept {
return get_uint64();
}
simdjson_inline bool number::is_int64() const noexcept {
return get_number_type() == number_type::signed_integer;
}
simdjson_inline int64_t number::get_int64() const noexcept {
return payload.signed_integer;
}
simdjson_inline number::operator int64_t() const noexcept {
return get_int64();
}
simdjson_inline bool number::is_double() const noexcept {
return get_number_type() == number_type::floating_point_number;
}
simdjson_inline double number::get_double() const noexcept {
return payload.floating_point_number;
}
simdjson_inline number::operator double() const noexcept {
return get_double();
}
simdjson_inline double number::as_double() const noexcept {
if(is_double()) {
return payload.floating_point_number;
}
if(is_int64()) {
return double(payload.signed_integer);
}
return double(payload.unsigned_integer);
}
simdjson_inline void number::append_s64(int64_t value) noexcept {
payload.signed_integer = value;
type = number_type::signed_integer;
}
simdjson_inline void number::append_u64(uint64_t value) noexcept {
payload.unsigned_integer = value;
type = number_type::unsigned_integer;
}
simdjson_inline void number::append_double(double value) noexcept {
payload.floating_point_number = value;
type = number_type::floating_point_number;
}
simdjson_inline void number::skip_double() noexcept {
type = number_type::floating_point_number;
}
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::json_type>::simdjson_result(rvv_vls::ondemand::json_type &&value) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::json_type>(std::forward<rvv_vls::ondemand::json_type>(value)) {}
simdjson_inline simdjson_result<rvv_vls::ondemand::json_type>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::json_type>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_TYPE_INL_H
/* end file simdjson/generic/ondemand/json_type-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/logger-inl.h for rvv_vls: #include "simdjson/generic/ondemand/logger-inl.h" */
/* begin file simdjson/generic/ondemand/logger-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/logger.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#include <memory>
#include <cstring>
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
namespace logger {
static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------";
static constexpr const int LOG_EVENT_LEN = 20;
static constexpr const int LOG_BUFFER_LEN = 30;
static constexpr const int LOG_SMALL_BUFFER_LEN = 10;
static int log_depth = 0; // Not threadsafe. Log only.
// Helper to turn unprintable or newline characters into spaces
static inline char printable_char(char c) {
if (c >= 0x20) {
return c;
} else {
return ' ';
}
}
template<typename... Args>
static inline std::string string_format(const std::string& format, const Args&... args)
{
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
auto size = static_cast<size_t>(size_s);
if (size <= 0) return std::string();
std::unique_ptr<char[]> buf(new char[size]);
std::snprintf(buf.get(), size, format.c_str(), args...);
SIMDJSON_POP_DISABLE_WARNINGS
return std::string(buf.get(), buf.get() + size - 1);
}
static inline log_level get_log_level_from_env()
{
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
char *lvl = getenv("SIMDJSON_LOG_LEVEL");
SIMDJSON_POP_DISABLE_WARNINGS
if (lvl && simdjson_strcasecmp(lvl, "ERROR") == 0) { return log_level::error; }
return log_level::info;
}
static inline log_level log_threshold()
{
static log_level threshold = get_log_level_from_env();
return threshold;
}
static inline bool should_log(log_level level)
{
return level >= log_threshold();
}
inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "", type, detail, log_level::info);
}
inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_line(iter, "", type, detail, delta, depth_delta, log_level::info);
}
inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept {
log_line(iter, index, depth, "+", type, detail, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_line(iter, "+", type, "", delta, depth_delta, log_level::info);
if (LOG_ENABLED) { log_depth++; }
}
inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
if (LOG_ENABLED) { log_depth--; }
log_line(iter, "-", type, "", delta, depth_delta, log_level::info);
}
inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_line(iter, "ERROR: ", error, detail, delta, depth_delta, log_level::error);
}
inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept {
log_line(iter, index, depth, "ERROR: ", error, detail, log_level::error);
}
inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_event(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept {
log_value(iter.json_iter(), type, detail, delta, depth_delta);
}
inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_start_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept {
log_end_value(iter.json_iter(), type, delta, depth_delta);
}
inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept {
log_error(iter.json_iter(), error, detail, delta, depth_delta);
}
inline void log_headers() noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(log_level::info))) {
// Technically a static variable is not thread-safe, but if you are using threads and logging... well...
static bool displayed_hint{false};
log_depth = 0;
printf("\n");
if (!displayed_hint) {
// We only print this helpful header once.
printf("# Logging provides the depth and position of the iterator user-visible steps:\n");
printf("# +array says 'this is where we were when we discovered the start array'\n");
printf(
"# -array says 'this is where we were when we ended the array'\n");
printf("# skip says 'this is a structural or value I am skipping'\n");
printf("# +/-skip says 'this is a start/end array or object I am skipping'\n");
printf("#\n");
printf("# The indentation of the terms (array, string,...) indicates the depth,\n");
printf("# in addition to the depth being displayed.\n");
printf("#\n");
printf("# Every token in the document has a single depth determined by the tokens before it,\n");
printf("# and is not affected by what the token actually is.\n");
printf("#\n");
printf("# Not all structural elements are presented as tokens in the logs.\n");
printf("#\n");
printf("# We never give control to the user within an empty array or an empty object.\n");
printf("#\n");
printf("# Inside an array, having a depth greater than the array's depth means that\n");
printf("# we are pointing inside a value.\n");
printf("# Having a depth equal to the array means that we are pointing right before a value.\n");
printf("# Having a depth smaller than the array means that we have moved beyond the array.\n");
displayed_hint = true;
}
printf("\n");
printf("| %-*s ", LOG_EVENT_LEN, "Event");
printf("| %-*s ", LOG_BUFFER_LEN, "Buffer");
printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next");
// printf("| %-*s ", 5, "Next#");
printf("| %-*s ", 5, "Depth");
printf("| Detail ");
printf("|\n");
printf("|%.*s", LOG_EVENT_LEN + 2, DASHES);
printf("|%.*s", LOG_BUFFER_LEN + 2, DASHES);
printf("|%.*s", LOG_SMALL_BUFFER_LEN + 2, DASHES);
// printf("|%.*s", 5+2, DASHES);
printf("|%.*s", 5 + 2, DASHES);
printf("|--------");
printf("|\n");
fflush(stdout);
}
}
}
template <typename... Args>
inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta, log_level level, Args&&... args) noexcept {
log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail, level, std::forward<Args>(args)...);
}
template <typename... Args>
inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail, log_level level, Args&&... args) noexcept {
if (LOG_ENABLED) {
if (simdjson_unlikely(should_log(level))) {
const int indent = depth * 2;
const auto buf = iter.token.buf;
auto msg = string_format(title, std::forward<Args>(args)...);
printf("| %*s%s%-*s ", indent, "", title_prefix,
LOG_EVENT_LEN - indent - int(strlen(title_prefix)), msg.c_str());
{
// Print the current structural.
printf("| ");
// Before we begin, the index might point right before the document.
// This could be unsafe, see https://github.com/simdjson/simdjson/discussions/1938
if (index < iter._root) {
printf("%*s", LOG_BUFFER_LEN, "");
} else {
auto current_structural = &buf[*index];
for (int i = 0; i < LOG_BUFFER_LEN; i++) {
printf("%c", printable_char(current_structural[i]));
}
}
printf(" ");
}
{
// Print the next structural.
printf("| ");
auto next_structural = &buf[*(index + 1)];
for (int i = 0; i < LOG_SMALL_BUFFER_LEN; i++) {
printf("%c", printable_char(next_structural[i]));
}
printf(" ");
}
// printf("| %5u ", *(index+1));
printf("| %5i ", depth);
printf("| %6.*s ", int(detail.size()), detail.data());
printf("|\n");
fflush(stdout);
}
}
}
} // namespace logger
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_LOGGER_INL_H
/* end file simdjson/generic/ondemand/logger-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/object-inl.h for rvv_vls: #include "simdjson/generic/ondemand/object-inl.h" */
/* begin file simdjson/generic/ondemand/object-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/jsonpathutil.h" */
/* amalgamation skipped (editor-only): #include <utility> */
/* amalgamation skipped (editor-only): #if SIMDJSON_SUPPORTS_CONCEPTS */
/* amalgamation skipped (editor-only): #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #if SIMDJSON_STATIC_REFLECTION */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string */
/* amalgamation skipped (editor-only): #include <meta> */
/* amalgamation skipped (editor-only): #endif */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept {
bool has_value;
SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return std::forward<object>(*this).find_field_unordered(key);
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool has_value;
SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) );
if (!has_value) {
logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data());
return NO_SUCH_FIELD;
}
return value(iter.child());
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, value>
simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) {
// Single pass driven directly by the value_iterator, mirroring
// find_field_unordered_raw + value(iter.child()). Compared to walking via
// object_iterator/field, this avoids constructing a simdjson_result<field> and
// a field (key + value) for every field -- and the development-check bookkeeping
// in object_iterator -- building a value only for the fields that actually match.
// We operate on a copy of the iterator, as object::begin() would.
#if SIMDJSON_DEVELOPMENT_CHECKS
// Mirror object::begin(): for_each must start at the beginning of the object,
// not from some position left behind by a prior find_field on the same object.
if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; }
#endif
value_iterator it = iter;
std::size_t matched = 0;
// Track which selector indices have already matched, as a compile-time bitset
// (one 64-bit word per 64 keys): the callback fires once per key, on its first
// occurrence, and we stop as soon as every key has matched.
constexpr std::size_t seen_words = (Selector::size() + 63) / 64;
std::array<std::uint64_t, seen_words> seen{};
while (it.is_open()) {
raw_json_string key;
error_code error;
std::size_t idx;
if constexpr (Selector::window.ok) {
// A window selector confirms a key from its raw bytes alone (the closing
// quote bounds it), so we take the length-free path: field_key (no backward
// scan, mirroring the ordered find_field) feeding match_raw(raw_json_string).
if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key);
} else {
// Otherwise derive the key length from the structural index (the following
// ':' token) to feed the length-aware hash, avoiding a forward SIMD scan.
std::size_t key_len;
if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; }
if ((error = it.field_value())) { it.abandon(); return {error, matched}; }
idx = Selector::match_raw(key.raw(), key_len);
}
if (idx < Selector::size()) {
const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63);
std::uint64_t &seen_word = seen[idx >> 6];
if (!(seen_word & seen_bit)) {
seen_word |= seen_bit;
value matched_value(it.child());
// The callback may return void or anything convertible to error_code
// (error_code itself, or a for_each_result from a nested for_each). When
// it yields an error_code, we stop at the first non-SUCCESS result and
// propagate it so the caller can surface value-parse errors (for example,
// a type mismatch on a matched field). A void-returning callback is
// responsible for handling its own errors.
if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) {
// Unlike the internal-error paths above, a callback error does not
// abandon the iterator: we leave it recoverable so the caller can keep
// using the object (or its parent) after handling the error.
if ((error = on_match(idx, matched_value))) { return {error, matched}; }
} else {
on_match(idx, matched_value);
}
if (++matched >= Selector::size()) { break; }
}
}
// Mirror object_iterator::operator++'s safety rail: if the callback consumed
// the value and left the iterator closed or in error (e.g. a void callback
// that swallowed a fatal sub-iteration error), stop here rather than calling
// skip_child on a closed iterator.
if (!it.is_open()) { break; }
// Skip the value (a no-op if the callback consumed it) and step to the next
// field; has_next_field() ends the container on '}', which closes the loop.
if ((error = it.skip_child())) { it.abandon(); return {error, matched}; }
if ((error = it.has_next_field().error())) { return {error, matched}; }
}
return {SUCCESS, matched};
}
namespace key_selector_for_each_detail {
// Dispatch a matched value to the I-th handler in the tuple (0-based). A handler
// is either an invocable taking the value (void- or error_code-returning) or a
// deserialization target, in which case we assign via value::get. We always
// return error_code so the core (index, value) for_each can treat the adapter
// uniformly.
template <typename Tuple, std::size_t... Is>
simdjson_really_inline error_code dispatch_value(
std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) {
error_code err = SUCCESS;
auto try_one = [&](auto Ic) {
constexpr std::size_t I = decltype(Ic)::value;
if (idx == I) {
auto&& h = std::get<I>(handlers);
using H = std::remove_reference_t<decltype(h)>;
if constexpr (std::is_invocable_v<H&, value>) {
// A handler returning void runs for its side effects; one returning
// anything convertible to error_code (error_code, or a for_each_result
// from a nested for_each) has its error captured and propagated.
if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) {
err = h(v);
} else {
h(v);
}
} else {
// Direct deserialization target: assign the matched value into it.
err = v.get(h);
}
}
};
(try_one(std::integral_constant<std::size_t, Is>{}), ...);
return err;
}
} // namespace key_selector_for_each_detail
template <typename Selector, typename... Handlers>
requires key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...);
// Reuse the single (index, value) implementation via a tiny adapter.
// The adapter is called once per *matched* key (very few); the hot path
// (iteration + match_raw + seen bitset) stays exactly the same.
return this->template for_each<Selector>(
[&](std::size_t i, value v) -> error_code {
return key_selector_for_each_detail::dispatch_value(
i, handlers, v, std::make_index_sequence<Selector::size()>{});
});
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline for_each_result object::for_each(Handlers&&... on_match)
noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
using Selector = key_selector<Keys...>;
return this->template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
#endif
simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_object().error() );
return object(iter);
}
simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.start_root_object().error() );
return object(iter);
}
simdjson_warn_unused simdjson_inline error_code object::consume() noexcept {
if(iter.is_at_key()) {
/**
* whenever you are pointing at a key, calling skip_child() is
* unsafe because you will hit a string and you will assume that
* it is string value, and this mistake will lead you to make bad
* depth computation.
*/
/**
* We want to 'consume' the key. We could really
* just do _json_iter->return_current_and_advance(); at this
* point, but, for clarity, we will use the high-level API to
* eat the key. We assume that the compiler optimizes away
* most of the work.
*/
simdjson_unused raw_json_string actual_key;
auto error = iter.field_key().get(actual_key);
if (error) { iter.abandon(); return error; };
// Let us move to the value while we are at it.
if ((error = iter.field_value())) { iter.abandon(); return error; }
}
auto error_skip = iter.json_iter().skip_child(iter.depth()-1);
if(error_skip) { iter.abandon(); }
return error_skip;
}
simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
const uint8_t * starting_point{iter.peek_start()};
auto error = consume();
if(error) { return error; }
const uint8_t * final_point{iter._json_iter->peek()};
return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
}
simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept {
SIMDJSON_TRY( iter.started_object().error() );
return object(iter);
}
simdjson_inline object object::resume(const value_iterator &iter) noexcept {
return iter;
}
simdjson_inline object::object(const value_iterator &_iter) noexcept
: iter{_iter}
{
}
simdjson_inline simdjson_result<object_iterator> object::begin() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
return object_iterator(iter, this);
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::begin() && noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// The object is a temporary that the iterator may outlive: do not lock it.
if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; }
#endif
return object_iterator(iter);
}
simdjson_inline simdjson_result<object_iterator> object::end() noexcept {
return object_iterator(iter);
}
inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept {
// An empty pointer has no json_pointer[0]: with a default-constructed
// std::string_view, reading it dereferences a null pointer.
if (json_pointer.empty() || json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
json_pointer = json_pointer.substr(1);
size_t slash = json_pointer.find('/');
std::string_view key = json_pointer.substr(0, slash);
// Grab the child with the given key
simdjson_result<value> child;
// If there is an escape character in the key, unescape it and then get the child.
size_t escape = key.find('~');
if (escape != std::string_view::npos) {
// Unescape the key
std::string unescaped(key);
do {
switch (unescaped[escape+1]) {
case '0':
unescaped.replace(escape, 2, "~");
break;
case '1':
unescaped.replace(escape, 2, "/");
break;
default:
return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer");
}
escape = unescaped.find('~', escape+1);
} while (escape != std::string::npos);
child = find_field(unescaped); // Take note find_field does not unescape keys when matching
} else {
child = find_field(key);
}
if(child.error()) {
return child; // we do not continue if there was an error
}
// If there is a /, we have to recurse and look up more of the path
if (slash != std::string_view::npos) {
child = child.at_pointer(json_pointer.substr(slash));
}
return child;
}
inline simdjson_result<value> 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);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, value>
#else
template <typename Func>
#endif
inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
auto result_pair = get_next_key_and_json_path(json_path);
std::string_view key = result_pair.first;
std::string_view remaining_path = result_pair.second;
// Empty key means the path segment could not be parsed (including malformed
// bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating
// "no progress" as a recursive lookup of "".
if (key.empty()) {
return INVALID_JSON_POINTER;
}
// Handle when its the case for wildcard
if (key == "*") {
// Loop through each field in the object
for (auto field : *this) {
value val;
SIMDJSON_TRY(field.value().get(val));
if (remaining_path.empty()) {
callback(val);
} else {
// A field that does not match the rest of the path is skipped.
error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback);
if (err && !internal::is_matching_failure_error(err)) { return err; }
}
}
return SUCCESS;
} else {
value val;
error_code error = find_field(key).get(val);
if (error) {
if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) {
return SUCCESS;
}
return error;
}
if (remaining_path.empty()) {
callback(val);
return SUCCESS;
} else {
return val.for_each_at_path_with_wildcard(remaining_path, callback);
}
}
}
simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept {
size_t count{0};
// Important: we do not consume any of the values.
for(simdjson_unused auto v : *this) { count++; }
// The above loop will always succeed, but we want to report errors.
if(iter.error()) { return iter.error(); }
// We need to move back at the start because we expect users to iterate through
// the object after counting the number of elements.
iter.reset_object();
return count;
}
simdjson_inline simdjson_result<bool> object::is_empty() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
bool is_not_empty;
auto error = iter.reset_object().get(is_not_empty);
if(error) { return error; }
return !is_not_empty;
}
simdjson_inline simdjson_result<bool> object::reset() & noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
return iter.reset_object();
}
simdjson_inline object_position object::get_current_position() const noexcept {
return object_position{iter.position(), iter.json_iter().depth()};
}
simdjson_inline error_code object::revert_position(object_position position) noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
if (locked) return OUT_OF_ORDER_ITERATION;
#endif
// json_iterator::reenter_child() requires the live depth to be exactly
// one level shallower than the target (matching how every other depth
// transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS
// additionally validates against the parser's per-depth container-start
// bookkeeping. Neither applies here: depending on what was captured and
// what has happened since (a scalar field fully consumed, a compound
// value left open, a find_field() miss that scanned past everything),
// the live depth when reverting can be any number of levels away from
// the captured one, and the captured depth is not necessarily a
// container's own start. reenter_at() moves directly, matching how
// reset_object() itself repositions without going through reenter_child().
iter.reenter_at(position.position, position.depth);
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline void object::set_locked(bool _locked) noexcept {
locked = _locked;
}
#endif
#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
template<constevalutil::fixed_string... FieldNames, typename T>
requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0))
simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept {
// Helper to check if a field name matches any of the requested fields
auto should_extract = [](std::string_view field_name) constexpr -> bool {
return ((FieldNames.view() == field_name) || ...);
};
// Iterate through all members of T using reflection
template for (constexpr auto mem : std::define_static_array(
std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) {
constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem));
// Only extract this field if it's in our list of requested fields
if constexpr (should_extract(key)) {
// Try to find and extract the field
if constexpr (concepts::optional_type<decltype(out.[:mem:])>) {
// For optional fields, it's ok if they're missing
auto field_result = find_field_unordered(key);
if (!field_result.error()) {
auto error = field_result.get(out.[:mem:]);
if (error && error != NO_SUCH_FIELD) {
return error;
}
} else if (field_result.error() != NO_SUCH_FIELD) {
return field_result.error();
} else {
out.[:mem:].reset();
}
} else {
// For required fields (in the requested list), fail if missing
SIMDJSON_TRY((*this)[key].get(out.[:mem:]));
}
}
}
};
return SUCCESS;
}
#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::object>::simdjson_result(rvv_vls::ondemand::object &&value) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::object>(std::forward<rvv_vls::ondemand::object>(value)) {}
simdjson_inline simdjson_result<rvv_vls::ondemand::object>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::object>(error) {}
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator> simdjson_result<rvv_vls::ondemand::object>::begin() & noexcept {
if (error()) { return error(); }
return first.begin();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator> simdjson_result<rvv_vls::ondemand::object>::begin() && noexcept {
if (error()) { return error(); }
return std::move(first).begin();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator> simdjson_result<rvv_vls::ondemand::object>::end() noexcept {
if (error()) { return error(); }
return first.end();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::object>::find_field_unordered(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field_unordered(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::object>::find_field_unordered(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<rvv_vls::ondemand::object>(first).find_field_unordered(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::object>::operator[](std::string_view key) & noexcept {
if (error()) { return error(); }
return first[key];
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::object>::operator[](std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<rvv_vls::ondemand::object>(first)[key];
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::object>::find_field(std::string_view key) & noexcept {
if (error()) { return error(); }
return first.find_field(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::object>::find_field(std::string_view key) && noexcept {
if (error()) { return error(); }
return std::forward<rvv_vls::ondemand::object>(first).find_field(key);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept {
if (error()) { return error(); }
return first.at_pointer(json_pointer);
}
simdjson_inline simdjson_result<rvv_vls::ondemand::value> simdjson_result<rvv_vls::ondemand::object>::at_path(
std::string_view json_path) noexcept {
if (error()) {
return error();
}
return first.at_path(json_path);
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Func>
requires std::invocable<Func, rvv_vls::ondemand::value>
#else
template <typename Func>
#endif
simdjson_inline error_code simdjson_result<rvv_vls::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept {
if (error()) { return error(); }
return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback));
}
#if SIMDJSON_SUPPORTS_CONCEPTS
template <typename Selector, typename Func>
requires rvv_vls::ondemand::key_selector_type<Selector> &&
std::is_invocable_v<Func&, std::size_t, rvv_vls::ondemand::value>
simdjson_inline rvv_vls::ondemand::for_each_result
simdjson_result<rvv_vls::ondemand::object>::for_each(Func&& on_match)
noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, rvv_vls::ondemand::value>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Func>(on_match));
}
template <typename Selector, typename... Handlers>
requires rvv_vls::ondemand::key_selector_type<Selector> &&
(sizeof...(Handlers) == Selector::size()) &&
(rvv_vls::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline rvv_vls::ondemand::for_each_result
simdjson_result<rvv_vls::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(rvv_vls::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Selector>(std::forward<Handlers>(on_match)...);
}
template <constevalutil::fixed_string... Keys, typename... Handlers>
requires (sizeof...(Handlers) == sizeof...(Keys)) &&
(sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) &&
(rvv_vls::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...)
simdjson_inline rvv_vls::ondemand::for_each_result
simdjson_result<rvv_vls::ondemand::object>::for_each(Handlers&&... on_match)
noexcept(rvv_vls::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) {
if (error()) { return {error(), 0}; }
return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...);
}
#endif
inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::object>::reset() noexcept {
if (error()) { return error(); }
return first.reset();
}
inline simdjson_result<rvv_vls::ondemand::object_position> simdjson_result<rvv_vls::ondemand::object>::get_current_position() noexcept {
if (error()) { return error(); }
return first.get_current_position();
}
inline error_code simdjson_result<rvv_vls::ondemand::object>::revert_position(rvv_vls::ondemand::object_position position) noexcept {
if (error()) { return error(); }
return first.revert_position(position);
}
inline simdjson_result<bool> simdjson_result<rvv_vls::ondemand::object>::is_empty() noexcept {
if (error()) { return error(); }
return first.is_empty();
}
simdjson_inline simdjson_result<size_t> simdjson_result<rvv_vls::ondemand::object>::count_fields() & noexcept {
if (error()) { return error(); }
return first.count_fields();
}
simdjson_inline simdjson_result<std::string_view> simdjson_result<rvv_vls::ondemand::object>::raw_json() noexcept {
if (error()) { return error(); }
return first.raw_json();
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H
/* end file simdjson/generic/ondemand/object-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/object_iterator-inl.h for rvv_vls: #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* begin file simdjson/generic/ondemand/object_iterator-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/field-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
//
// object_iterator
//
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept
: iter{_iter}
{}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::object_iterator(const value_iterator &_iter, object* _parent) noexcept
: parent{_parent}, iter{_iter}
{
if (parent) parent->set_locked(true);
}
#endif
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline object_iterator::~object_iterator() noexcept
{
if (parent) parent->set_locked(false);
}
simdjson_inline object_iterator::object_iterator(object_iterator&& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{other.parent},
iter{std::move(other.iter)}
{
other.parent = nullptr;
}
simdjson_inline object_iterator& object_iterator::operator=(object_iterator&& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = other.parent;
iter = std::move(other.iter);
other.parent = nullptr;
}
return *this;
}
simdjson_inline object_iterator::object_iterator(const object_iterator& other) noexcept
: has_been_referenced{other.has_been_referenced},
parent{nullptr},
iter{other.iter}
{}
simdjson_inline object_iterator& object_iterator::operator=(const object_iterator& other) noexcept {
if (this != &other)
{
if (parent)
parent->set_locked(false);
has_been_referenced = other.has_been_referenced;
parent = nullptr;
iter = other.iter;
}
return *this;
}
#endif
simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// We must call * once per iteration.
SIMDJSON_ASSUME(!has_been_referenced);
has_been_referenced = true;
#endif
error_code error = iter.error();
if (error) { iter.abandon(); return error; }
auto result = field::start(iter);
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (result.error()) { iter.abandon(); }
return result;
}
simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept {
return !(*this != other);
}
simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept {
return iter.is_open();
}
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
simdjson_inline object_iterator &object_iterator::operator++() noexcept {
#if SIMDJSON_DEVELOPMENT_CHECKS
// Before calling ++, we must have called *.
SIMDJSON_ASSUME(has_been_referenced);
has_been_referenced = false;
#endif
// TODO this is a safety rail ... users should exit loops as soon as they receive an error.
// Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free.
if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error
simdjson_unused error_code error;
if ((error = iter.skip_child() )) { return *this; }
simdjson_unused bool has_value;
if ((error = iter.has_next_field().get(has_value) )) { return *this; };
return *this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// ### Live States
//
// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is
// always SUCCESS:
//
// - Start: This is the state when the object is first found and the iterator is just past the {.
// In this state, at_start == true.
// - Next: After we hand a scalar value to the user, or an array/object which they then fully
// iterate over, the iterator is at the , or } before the next value. In this state,
// depth == iter.depth, at_start == false, and error == SUCCESS.
// - Unfinished Business: When we hand an array/object to the user which they do not fully
// iterate over, we need to finish that iteration by skipping child values until we reach the
// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS.
//
// ## Error States
//
// In error states, we will yield exactly one more value before stopping. iter.depth == depth
// and at_start is always false. We decrement after yielding the error, moving to the Finished
// state.
//
// - Chained Error: When the object iterator is part of an error chain--for example, in
// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an
// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
// iter.depth == depth, and at_start == false. We decrement depth when we yield the error.
// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields,
// we flag that as an error and treat it exactly the same as a Chained Error. In this state,
// error == TAPE_ERROR, iter.depth == depth, and at_start == false.
//
// Errors that occur while reading a field to give to the user (such as when the key is not a
// string or the field is missing a colon) are yielded immediately. Depth is then decremented,
// moving to the Finished state without transitioning through an Error state at all.
//
// ## Terminal State
//
// The terminal state has iter.depth < depth. at_start is always false.
//
// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth.
// In this state, iter.depth < depth, at_start == false, and error == SUCCESS.
//
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator>::simdjson_result(
rvv_vls::ondemand::object_iterator &&value
) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::object_iterator>(std::forward<rvv_vls::ondemand::object_iterator>(value))
{
first.iter.assert_is_valid();
}
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::object_iterator>({}, error)
{
}
simdjson_inline simdjson_result<rvv_vls::ondemand::field> simdjson_result<rvv_vls::ondemand::object_iterator>::operator*() noexcept {
if (error()) { return error(); }
return *first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<rvv_vls::ondemand::object_iterator>::operator==(const simdjson_result<rvv_vls::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return !error(); }
return first == other.first;
}
// If we're iterating and there is an error, return the error once.
simdjson_inline bool simdjson_result<rvv_vls::ondemand::object_iterator>::operator!=(const simdjson_result<rvv_vls::ondemand::object_iterator> &other) const noexcept {
if (!first.iter.is_valid()) { return error(); }
return first != other.first;
}
// Checks for ']' and ','
simdjson_inline simdjson_result<rvv_vls::ondemand::object_iterator> &simdjson_result<rvv_vls::ondemand::object_iterator>::operator++() noexcept {
// Clear the error if there is one, so we don't yield it twice
if (error()) { second = SUCCESS; return *this; }
++first;
return *this;
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_ITERATOR_INL_H
/* end file simdjson/generic/ondemand/object_iterator-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/ranges-inl.h for rvv_vls: #include "simdjson/generic/ondemand/ranges-inl.h" */
/* begin file simdjson/generic/ondemand/ranges-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/ranges.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object_iterator-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
#if SIMDJSON_SUPPORTS_RANGES
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
//
// array_range_iterator
//
simdjson_inline array_range_iterator::array_range_iterator(array_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<value> array_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline array_range_iterator& array_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void array_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// array_range
//
simdjson_inline array_range::array_range(array& arr) noexcept {
auto b = arr.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = arr.end().value_unsafe();
}
simdjson_inline array_range_iterator array_range::begin() noexcept {
return array_range_iterator(begin_);
}
simdjson_inline array_range_iterator array_range::end() noexcept {
return array_range_iterator(end_);
}
//
// object_range_iterator
//
simdjson_inline object_range_iterator::object_range_iterator(object_iterator iter) noexcept
: iter_{iter} {}
simdjson_inline simdjson_result<field> object_range_iterator::operator*() const noexcept {
return *iter_;
}
simdjson_inline object_range_iterator& object_range_iterator::operator++() noexcept {
++iter_;
return *this;
}
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
simdjson_inline void object_range_iterator::operator++(int) noexcept {
++*this;
}
SIMDJSON_POP_DISABLE_WARNINGS
//
// object_range
//
simdjson_inline object_range::object_range(object& obj) noexcept {
auto b = obj.begin();
if (b.error()) { error_ = b.error(); return; }
begin_ = b.value_unsafe();
end_ = obj.end().value_unsafe();
}
simdjson_inline object_range_iterator object_range::begin() noexcept {
return object_range_iterator(begin_);
}
simdjson_inline object_range_iterator object_range::end() noexcept {
return object_range_iterator(end_);
}
//
// Free functions
//
simdjson_inline array_range get_range(array& arr) noexcept {
return array_range(arr);
}
simdjson_inline object_range get_key_value_range(object& obj) noexcept {
return object_range(obj);
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline array_range get_range(simdjson_result<array> result) {
return array_range(result.value());
}
simdjson_inline object_range get_key_value_range(simdjson_result<object> result) {
return object_range(result.value());
}
#endif // SIMDJSON_EXCEPTIONS
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
// Verify the range wrapper types satisfy the expected C++20 concepts.
static_assert(std::input_iterator<simdjson::rvv_vls::ondemand::array_range_iterator>);
static_assert(std::input_iterator<simdjson::rvv_vls::ondemand::object_range_iterator>);
static_assert(std::ranges::input_range<simdjson::rvv_vls::ondemand::array_range>);
static_assert(std::ranges::input_range<simdjson::rvv_vls::ondemand::object_range>);
static_assert(std::ranges::view<simdjson::rvv_vls::ondemand::array_range>);
static_assert(std::ranges::view<simdjson::rvv_vls::ondemand::object_range>);
#endif // SIMDJSON_SUPPORTS_RANGES
#endif // SIMDJSON_GENERIC_ONDEMAND_RANGES_INL_H
/* end file simdjson/generic/ondemand/ranges-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/parser-inl.h for rvv_vls: #include "simdjson/generic/ondemand/parser-inl.h" */
/* begin file simdjson/generic/ondemand/parser-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/padded_string_view.h" */
/* amalgamation skipped (editor-only): #include "simdjson/implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/internal/dom_parser_implementation.h" */
/* amalgamation skipped (editor-only): #include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/parser.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
simdjson_inline parser::parser(size_t max_capacity) noexcept
: _max_capacity{max_capacity} {
}
simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept {
if (new_capacity > max_capacity()) { return CAPACITY; }
if (string_buf && new_capacity <= capacity() && new_max_depth <= max_depth()) { return SUCCESS; }
// string_capacity copied from document::allocate
_capacity = 0;
if(5 * (new_capacity / 3) + SIMDJSON_PADDING < SIMDJSON_PADDING) {
return CAPACITY; // overflow, only happen on legacy 32-bit systems with very large capacity
}
size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * (new_capacity / 3) + SIMDJSON_PADDING, 64);
string_buf.reset(new (std::nothrow) uint8_t[string_capacity]);
#if SIMDJSON_DEVELOPMENT_CHECKS
start_positions.reset(new (std::nothrow) token_position[new_max_depth]);
#endif
if (implementation) {
SIMDJSON_TRY( implementation->set_capacity(new_capacity) );
SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) );
} else {
SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) );
}
_capacity = new_capacity;
_max_depth = new_max_depth;
return SUCCESS;
}
#if SIMDJSON_DEVELOPMENT_CHECKS
simdjson_inline simdjson_warn_unused bool parser::string_buffer_overflow(const uint8_t *string_buf_loc) const noexcept {
return (string_buf_loc < string_buf.get()) || (size_t(string_buf_loc - string_buf.get()) >= capacity());
}
#endif
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this });
}
#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate_allow_incomplete_json(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
_document_len = json.length();
// Allocate if needed
if (capacity() < json.length() || !string_buf) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
const simdjson::error_code err = implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular);
if (err) {
if (err != UNCLOSED_STRING)
return err;
}
return document::start({ reinterpret_cast<const uint8_t *>(json.data()), this, true });
}
#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const char *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept {
return iterate(padded_string_view(json, len, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string_view json, size_t allocated) & noexcept {
return iterate(padded_string_view(json, allocated));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(std::string &json) & noexcept {
return iterate(pad_with_reserve(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const std::string &json) & noexcept {
return iterate(padded_string_view(json));
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string_view> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
padded_string_view json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<document> parser::iterate(const simdjson_result<padded_string> &result) & noexcept {
// We don't presently have a way to temporarily get a const T& from a simdjson_result<T> without throwing an exception
SIMDJSON_TRY( result.error() );
const padded_string &json = result.value_unsafe();
return iterate(json);
}
simdjson_warn_unused simdjson_inline simdjson_result<json_iterator> parser::iterate_raw(padded_string_view json) & noexcept {
if (!json.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
json.remove_utf8_bom();
// Allocate if needed
if (capacity() < json.length()) {
SIMDJSON_TRY( allocate(json.length(), max_depth()) );
}
// Run stage 1.
SIMDJSON_TRY( implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.length(), stage1_mode::regular) );
return json_iterator(reinterpret_cast<const uint8_t *>(json.data()), this);
}
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size) noexcept {
return iterate_many(padded_string_view(s), batch_size);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size) noexcept {
return iterate_many(pad(s), batch_size);
}
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
SIMDJSON_PUSH_DISABLE_WARNINGS
SIMDJSON_DISABLE_DEPRECATED_WARNING
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
// Map allow_comma_separated to stream_format::comma_delimited
if (allow_comma_separated) {
return document_stream(*this, buf, len, batch_size, false, stream_format::comma_delimited);
}
return document_stream(*this, buf, len, batch_size, false, stream_format::whitespace_delimited);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, bool allow_comma_separated) noexcept {
// Warning: no check is done on the buffer padding. We trust the user.
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, bool allow_comma_separated) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(padded_string_view(s), batch_size, allow_comma_separated);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, bool allow_comma_separated) noexcept {
return iterate_many(pad(s), batch_size, allow_comma_separated);
}
SIMDJSON_POP_DISABLE_WARNINGS
#endif // SIMDJSON_DISABLE_DEPRECATED_API
inline simdjson_result<document_stream> parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size, stream_format format) noexcept {
if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; }
if((len >= 3) && (std::memcmp(buf, "\xEF\xBB\xBF", 3) == 0)) {
buf += 3;
len -= 3;
}
if (format == stream_format::comma_delimited_array) {
// Strip leading JSON whitespace.
while (len > 0 && (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\n' || buf[0] == '\r')) {
buf++; len--;
}
// Expect the opening '['.
if (len == 0 || buf[0] != '[') { return TAPE_ERROR; }
buf++; len--;
// Strip trailing JSON whitespace.
while (len > 0 && (buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r')) {
len--;
}
// Expect the closing ']'.
if (len == 0 || buf[len-1] != ']') { return TAPE_ERROR; }
len--;
// Fall through to comma_delimited over the array contents.
format = stream_format::comma_delimited;
}
return document_stream(*this, buf, len, batch_size, false, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const char *buf, size_t len, size_t batch_size, stream_format format) noexcept {
return iterate_many(reinterpret_cast<const uint8_t *>(buf), len, batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(padded_string_view s, size_t batch_size, stream_format format) noexcept {
if (!s.has_sufficient_padding()) { return INSUFFICIENT_PADDING; }
return iterate_many(s.data(), s.length(), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(std::string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(pad(s), batch_size, format);
}
inline simdjson_result<document_stream> parser::iterate_many(const padded_string &s, size_t batch_size, stream_format format) noexcept {
return iterate_many(padded_string_view(s), batch_size, format);
}
simdjson_pure simdjson_inline size_t parser::capacity() const noexcept {
return _capacity;
}
simdjson_pure simdjson_inline size_t parser::max_capacity() const noexcept {
return _max_capacity;
}
simdjson_pure simdjson_inline size_t parser::max_depth() const noexcept {
return _max_depth;
}
simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept {
if(max_capacity > dom::MINIMAL_DOCUMENT_CAPACITY) {
_max_capacity = max_capacity;
} else {
_max_capacity = dom::MINIMAL_DOCUMENT_CAPACITY;
}
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape(raw_json_string in, uint8_t *&dst, bool allow_replacement) const noexcept {
uint8_t *end = implementation->parse_string(in.buf, dst, allow_replacement);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> parser::unescape_wobbly(raw_json_string in, uint8_t *&dst) const noexcept {
uint8_t *end = implementation->parse_wobbly_string(in.buf, dst);
if (!end) { return STRING_ERROR; }
std::string_view result(reinterpret_cast<const char *>(dst), end-dst);
dst = end;
return result;
}
simdjson_inline simdjson_warn_unused ondemand::parser& parser::get_parser() {
return *parser::get_parser_instance();
}
simdjson_inline bool parser::release_parser() {
auto &parser_instance = parser::get_threadlocal_parser_if_exists();
if (parser_instance) {
parser_instance.reset();
return true;
}
return false;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_parser_instance() {
std::unique_ptr<ondemand::parser>& parser_instance = get_threadlocal_parser_if_exists();
if (!parser_instance) {
parser_instance.reset(new ondemand::parser());
}
return parser_instance;
}
simdjson_inline simdjson_warn_unused std::unique_ptr<ondemand::parser>& parser::get_threadlocal_parser_if_exists() {
// @the-moisrex points out that this could be implemented with std::optional (C++17).
thread_local std::unique_ptr<ondemand::parser> parser_instance = nullptr;
return parser_instance;
}
} // namespace ondemand
} // namespace rvv_vls
} // namespace simdjson
namespace simdjson {
simdjson_inline simdjson_result<rvv_vls::ondemand::parser>::simdjson_result(rvv_vls::ondemand::parser &&value) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::parser>(std::forward<rvv_vls::ondemand::parser>(value)) {}
simdjson_inline simdjson_result<rvv_vls::ondemand::parser>::simdjson_result(error_code error) noexcept
: implementation_simdjson_result_base<rvv_vls::ondemand::parser>(error) {}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_ONDEMAND_PARSER_INL_H
/* end file simdjson/generic/ondemand/parser-inl.h for rvv_vls */
/* including simdjson/generic/ondemand/raw_json_string-inl.h for rvv_vls: #include "simdjson/generic/ondemand/raw_json_string-inl.h" */
/* begin file simdjson/generic/ondemand/raw_json_string-inl.h for rvv_vls */
#ifndef SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H
/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */
/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */
/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */
/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */
namespace simdjson {
namespace rvv_vls {
namespace ondemand {
simdjson_inline raw_json_string::raw_json_string(const uint8_t * _buf) noexcept : buf{_buf} {}
simdjson_inline const char * raw_json_string::raw() const noexcept {
return reinterpret_cast<const char *>(buf);
}
simdjson_inline char raw_json_string::operator[](size_t i) const noexcept {
return reinterpret_cast<const char *>(buf)[i];
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept {
size_t pos{0};
while(pos < target.size()) {
pos = target.find('"', pos);
if(pos == std::string_view::npos) { return true; }
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept {
size_t pos{0};
while(target[pos]) {
const char * result = strchr(target+pos, '"');
if(result == nullptr) { return true; }
pos = result - target;
if(pos != 0 && target[pos-1] != '\\') { return false; }
if(pos > 1 && target[pos-2] == '\\') {
size_t backslash_count{2};
for(size_t i = 3; i <= pos; i++) {
if(target[pos-i] == '\\') { backslash_count++; }
else { break; }
}
if(backslash_count % 2 == 0) { return false; }
}
pos++;
}
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept {
// If we are going to call memcmp, then we must know something about the length of the raw_json_string.
return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept {
// Assumptions: does not contain unescaped quote characters("), and
// the raw content is quote terminated within a valid JSON string.
if(target.size() <= SIMDJSON_PADDING) {
return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size());
}
// Past SIMDJSON_PADDING we can no longer rely on the padding to keep the
// comparison in bounds, so we must stop at the quote terminating the key.
return is_equal(target);
}
simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept {
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;pos < target.size();pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept {
// Assumptions: 'target' does not contain unescaped quote characters, is null terminated and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept {
// Assumptions: does not contain unescaped quote characters, and
// the raw content is quote terminated within a valid JSON string.
const char * r{raw()};
size_t pos{0};
bool escaping{false};
for(;target[pos];pos++) {
if(r[pos] != target[pos]) { return false; }
// if target is a compile-time constant and it is free from
// quotes, then the next part could get optimized away through
// inlining.
if((target[pos] == '"') && !escaping) {
// We have reached the end of the raw_json_string but
// the target is not done.
return false;
} else if(target[pos] == '\\') {
escaping = !escaping;
} else {
escaping = false;
}
}
if(r[pos] != '"') { return false; }
return true;
}
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept {
return a.unsafe_is_equal(c);
}
simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept {
return a == c;
}
simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept {
return !(a == c);
}
simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept {
return !(a == c);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept {
return iter.unescape(*this, allow_replacement);
}
simdjson_inline simdjson_warn_unused simdjson_result<std::string_view> raw_json_string::unescape_wobbly(json_iterator &